| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # coding=utf-8 | 2 # coding=utf-8 |
| 3 # Copyright 2013 The LUCI Authors. All rights reserved. | 3 # Copyright 2013 The LUCI Authors. All rights reserved. |
| 4 # Use of this source code is governed under the Apache License, Version 2.0 | 4 # Use of this source code is governed under the Apache License, Version 2.0 |
| 5 # that can be found in the LICENSE file. | 5 # that can be found in the LICENSE file. |
| 6 | 6 |
| 7 import base64 | 7 import base64 |
| 8 import json | 8 import json |
| 9 import logging | 9 import logging |
| 10 import os | 10 import os |
| 11 import re | 11 import re |
| 12 import signal | 12 import signal |
| 13 import sys | 13 import sys |
| 14 import tempfile | 14 import tempfile |
| 15 import time | 15 import time |
| 16 import unittest | 16 import unittest |
| 17 | 17 |
| 18 import test_env_bot_code | 18 import test_env_bot_code |
| 19 test_env_bot_code.setup_test_env() | 19 test_env_bot_code.setup_test_env() |
| 20 | 20 |
| 21 # Creates a server mock for functions in net.py. | 21 # Creates a server mock for functions in net.py. |
| 22 import net_utils | 22 import net_utils |
| 23 | 23 |
| 24 from api import os_utilities | |
| 25 from depot_tools import fix_encoding | 24 from depot_tools import fix_encoding |
| 26 from utils import file_path | 25 from utils import file_path |
| 27 from utils import large | 26 from utils import large |
| 28 from utils import logging_utils | 27 from utils import logging_utils |
| 29 from utils import subprocess42 | 28 from utils import subprocess42 |
| 30 from utils import tools | |
| 31 import fake_swarming | 29 import fake_swarming |
| 32 import task_runner | 30 import task_runner |
| 33 | 31 |
| 34 CLIENT_DIR = os.path.normpath( | 32 CLIENT_DIR = os.path.normpath( |
| 35 os.path.join(test_env_bot_code.BOT_DIR, '..', '..', '..', 'client')) | 33 os.path.join(test_env_bot_code.BOT_DIR, '..', '..', '..', 'client')) |
| 36 | 34 |
| 37 sys.path.insert(0, os.path.join(CLIENT_DIR, 'tests')) | 35 sys.path.insert(0, os.path.join(CLIENT_DIR, 'tests')) |
| 38 import isolateserver_mock | 36 import isolateserver_mock |
| 39 | 37 |
| 40 | 38 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 77 file_path.rmtree(self.root_dir) | 75 file_path.rmtree(self.root_dir) |
| 78 except OSError: | 76 except OSError: |
| 79 print >> sys.stderr, 'Failed to delete %s' % self.root_dir | 77 print >> sys.stderr, 'Failed to delete %s' % self.root_dir |
| 80 finally: | 78 finally: |
| 81 super(TestTaskRunnerBase, self).tearDown() | 79 super(TestTaskRunnerBase, self).tearDown() |
| 82 | 80 |
| 83 @classmethod | 81 @classmethod |
| 84 def get_task_details(cls, *args, **kwargs): | 82 def get_task_details(cls, *args, **kwargs): |
| 85 return task_runner.TaskDetails(get_manifest(*args, **kwargs)) | 83 return task_runner.TaskDetails(get_manifest(*args, **kwargs)) |
| 86 | 84 |
| 87 def gen_requests(self, cost_usd=0., **kwargs): | 85 def gen_requests(self, cost_usd=0., auth_headers=None, **kwargs): |
| 88 return [ | 86 return [ |
| 89 ( | 87 ( |
| 90 'https://localhost:1/swarming/api/v1/bot/task_update/23', | 88 'https://localhost:1/swarming/api/v1/bot/task_update/23', |
| 91 self.get_check_first(cost_usd), | 89 self.get_check_first(cost_usd, auth_headers=auth_headers), |
| 92 {'ok': True}, | 90 {'ok': True}, |
| 93 ), | 91 ), |
| 94 ( | 92 ( |
| 95 'https://localhost:1/swarming/api/v1/bot/task_update/23', | 93 'https://localhost:1/swarming/api/v1/bot/task_update/23', |
| 96 self.get_check_final(**kwargs), | 94 self.get_check_final(auth_headers=auth_headers, **kwargs), |
| 97 {'ok': True}, | 95 {'ok': True}, |
| 98 ), | 96 ), |
| 99 ] | 97 ] |
| 100 | 98 |
| 101 def requests(self, **kwargs): | 99 def requests(self, **kwargs): |
| 102 """Generates the expected HTTP requests for a task run.""" | 100 """Generates the expected HTTP requests for a task run.""" |
| 103 self.expected_requests(self.gen_requests(**kwargs)) | 101 self.expected_requests(self.gen_requests(**kwargs)) |
| 104 | 102 |
| 105 def get_check_first(self, cost_usd): | 103 def get_check_first(self, cost_usd, auth_headers=None): |
| 106 def check_first(kwargs): | 104 def check_first(kwargs): |
| 107 self.assertLessEqual(cost_usd, kwargs['data'].pop('cost_usd')) | 105 self.assertLessEqual(cost_usd, kwargs['data'].pop('cost_usd')) |
| 108 self.assertEqual( | 106 self.assertEqual( |
| 109 { | 107 { |
| 110 'data': { | 108 'data': { |
| 111 'id': 'localhost', | 109 'id': 'localhost', |
| 112 'task_id': 23, | 110 'task_id': 23, |
| 113 }, | 111 }, |
| 112 'follow_redirects': False, |
| 113 'headers': auth_headers or {}, |
| 114 }, | 114 }, |
| 115 kwargs) | 115 kwargs) |
| 116 return check_first | 116 return check_first |
| 117 | 117 |
| 118 | 118 |
| 119 class TestTaskRunner(TestTaskRunnerBase): | 119 class TestTaskRunner(TestTaskRunnerBase): |
| 120 def setUp(self): | 120 def setUp(self): |
| 121 super(TestTaskRunner, self).setUp() | 121 super(TestTaskRunner, self).setUp() |
| 122 self.mock(time, 'time', lambda: 1000000000.) | 122 self.mock(time, 'time', lambda: 1000000000.) |
| 123 | 123 |
| 124 def get_check_final(self, exit_code=0, output_re=r'^hi\n$', outputs_ref=None): | 124 def get_check_final( |
| 125 self, exit_code=0, output_re=r'^hi\n$', outputs_ref=None, |
| 126 auth_headers=None): |
| 125 def check_final(kwargs): | 127 def check_final(kwargs): |
| 126 # Ignore these values. | 128 # Ignore these values. |
| 127 kwargs['data'].pop('bot_overhead', None) | 129 kwargs['data'].pop('bot_overhead', None) |
| 128 kwargs['data'].pop('duration', None) | 130 kwargs['data'].pop('duration', None) |
| 129 | 131 |
| 130 output = '' | 132 output = '' |
| 131 if 'output' in kwargs['data']: | 133 if 'output' in kwargs['data']: |
| 132 output = base64.b64decode(kwargs['data'].pop('output')) | 134 output = base64.b64decode(kwargs['data'].pop('output')) |
| 133 self.assertTrue(re.match(output_re, output)) | 135 self.assertTrue(re.match(output_re, output)) |
| 134 | 136 |
| 135 expected = { | 137 expected = { |
| 136 'data': { | 138 'data': { |
| 137 'cost_usd': 10., | 139 'cost_usd': 10., |
| 138 'exit_code': exit_code, | 140 'exit_code': exit_code, |
| 139 'hard_timeout': False, | 141 'hard_timeout': False, |
| 140 'id': 'localhost', | 142 'id': 'localhost', |
| 141 'io_timeout': False, | 143 'io_timeout': False, |
| 142 'output_chunk_start': 0, | 144 'output_chunk_start': 0, |
| 143 'task_id': 23, | 145 'task_id': 23, |
| 144 }, | 146 }, |
| 147 'follow_redirects': False, |
| 148 'headers': auth_headers or {}, |
| 145 } | 149 } |
| 146 if outputs_ref: | 150 if outputs_ref: |
| 147 expected['data']['outputs_ref'] = outputs_ref | 151 expected['data']['outputs_ref'] = outputs_ref |
| 148 self.assertEqual(expected, kwargs) | 152 self.assertEqual(expected, kwargs) |
| 149 return check_final | 153 return check_final |
| 150 | 154 |
| 151 def _run_command(self, task_details): | 155 def _run_command(self, task_details, auth_headers_file=None): |
| 152 start = time.time() | 156 start = time.time() |
| 153 self.mock(time, 'time', lambda: start + 10) | 157 self.mock(time, 'time', lambda: start + 10) |
| 154 server = 'https://localhost:1' | 158 server = 'https://localhost:1' |
| 155 return task_runner.run_command( | 159 return task_runner.run_command( |
| 156 server, task_details, self.work_dir, 3600., start, 1) | 160 server, task_details, self.work_dir, auth_headers_file, 3600., start, 1) |
| 157 | 161 |
| 158 def test_load_and_run_raw(self): | 162 def test_load_and_run_raw(self): |
| 159 server = 'https://localhost:1' | 163 server = 'https://localhost:1' |
| 160 | 164 |
| 161 def run_command( | 165 def run_command( |
| 162 swarming_server, task_details, work_dir, cost_usd_hour, start, | 166 swarming_server, task_details, work_dir, auth_headers_file, |
| 163 min_free_space): | 167 cost_usd_hour, start, min_free_space): |
| 164 self.assertEqual(server, swarming_server) | 168 self.assertEqual(server, swarming_server) |
| 169 self.assertTrue(isinstance(task_details, task_runner.TaskDetails)) |
| 165 # Necessary for OSX. | 170 # Necessary for OSX. |
| 166 self.assertEqual( | 171 self.assertEqual( |
| 167 os.path.realpath(self.work_dir), os.path.realpath(work_dir)) | 172 os.path.realpath(self.work_dir), os.path.realpath(work_dir)) |
| 168 self.assertTrue(isinstance(task_details, task_runner.TaskDetails)) | 173 self.assertEqual(None, auth_headers_file) |
| 169 self.assertEqual(3600., cost_usd_hour) | 174 self.assertEqual(3600., cost_usd_hour) |
| 170 self.assertEqual(time.time(), start) | 175 self.assertEqual(time.time(), start) |
| 171 self.assertEqual(1, min_free_space) | 176 self.assertEqual(1, min_free_space) |
| 172 return { | 177 return { |
| 173 u'exit_code': 1, | 178 u'exit_code': 1, |
| 174 u'hard_timeout': False, | 179 u'hard_timeout': False, |
| 175 u'io_timeout': False, | 180 u'io_timeout': False, |
| 176 u'must_signal_internal_failure': None, | 181 u'must_signal_internal_failure': None, |
| 177 u'version': task_runner.OUT_VERSION, | 182 u'version': task_runner.OUT_VERSION, |
| 178 } | 183 } |
| 179 self.mock(task_runner, 'run_command', run_command) | 184 self.mock(task_runner, 'run_command', run_command) |
| 180 | 185 |
| 181 manifest = os.path.join(self.root_dir, 'manifest') | 186 manifest = os.path.join(self.root_dir, 'manifest') |
| 182 with open(manifest, 'wb') as f: | 187 with open(manifest, 'wb') as f: |
| 183 data = { | 188 data = { |
| 184 'bot_id': 'localhost', | 189 'bot_id': 'localhost', |
| 185 'command': ['a'], | 190 'command': ['a'], |
| 186 'env': {'d': 'e'}, | 191 'env': {'d': 'e'}, |
| 187 'extra_args': [], | 192 'extra_args': [], |
| 188 'grace_period': 30., | 193 'grace_period': 30., |
| 189 'hard_timeout': 10, | 194 'hard_timeout': 10, |
| 190 'io_timeout': 11, | 195 'io_timeout': 11, |
| 191 'isolated': None, | 196 'isolated': None, |
| 192 'task_id': 23, | 197 'task_id': 23, |
| 193 } | 198 } |
| 194 json.dump(data, f) | 199 json.dump(data, f) |
| 195 | 200 |
| 196 out_file = os.path.join(self.root_dir, 'work', 'task_runner_out.json') | 201 out_file = os.path.join(self.root_dir, 'work', 'task_runner_out.json') |
| 197 task_runner.load_and_run(manifest, server, 3600., time.time(), out_file, 1) | 202 task_runner.load_and_run( |
| 203 manifest, server, None, 3600., time.time(), out_file, 1) |
| 198 expected = { | 204 expected = { |
| 199 u'exit_code': 1, | 205 u'exit_code': 1, |
| 200 u'hard_timeout': False, | 206 u'hard_timeout': False, |
| 201 u'io_timeout': False, | 207 u'io_timeout': False, |
| 202 u'must_signal_internal_failure': None, | 208 u'must_signal_internal_failure': None, |
| 203 u'version': task_runner.OUT_VERSION, | 209 u'version': task_runner.OUT_VERSION, |
| 204 } | 210 } |
| 205 with open(out_file, 'rb') as f: | 211 with open(out_file, 'rb') as f: |
| 206 self.assertEqual(expected, json.load(f)) | 212 self.assertEqual(expected, json.load(f)) |
| 207 | 213 |
| 208 def test_load_and_run_isolated(self): | 214 def test_load_and_run_isolated(self): |
| 209 self.expected_requests([]) | 215 self.expected_requests([]) |
| 210 server = 'https://localhost:1' | 216 server = 'https://localhost:1' |
| 211 | 217 |
| 212 def run_command( | 218 def run_command( |
| 213 swarming_server, task_details, work_dir, cost_usd_hour, start, | 219 swarming_server, task_details, work_dir, auth_headers_file, |
| 214 min_free_space): | 220 cost_usd_hour, start, min_free_space): |
| 215 self.assertEqual(server, swarming_server) | 221 self.assertEqual(server, swarming_server) |
| 222 self.assertTrue(isinstance(task_details, task_runner.TaskDetails)) |
| 216 # Necessary for OSX. | 223 # Necessary for OSX. |
| 217 self.assertEqual( | 224 self.assertEqual( |
| 218 os.path.realpath(self.work_dir), os.path.realpath(work_dir)) | 225 os.path.realpath(self.work_dir), os.path.realpath(work_dir)) |
| 219 self.assertTrue(isinstance(task_details, task_runner.TaskDetails)) | 226 self.assertEqual(None, auth_headers_file) |
| 220 self.assertEqual(3600., cost_usd_hour) | 227 self.assertEqual(3600., cost_usd_hour) |
| 221 self.assertEqual(time.time(), start) | 228 self.assertEqual(time.time(), start) |
| 222 self.assertEqual(1, min_free_space) | 229 self.assertEqual(1, min_free_space) |
| 223 return { | 230 return { |
| 224 u'exit_code': 0, | 231 u'exit_code': 0, |
| 225 u'hard_timeout': False, | 232 u'hard_timeout': False, |
| 226 u'io_timeout': False, | 233 u'io_timeout': False, |
| 227 u'must_signal_internal_failure': None, | 234 u'must_signal_internal_failure': None, |
| 228 u'version': task_runner.OUT_VERSION, | 235 u'version': task_runner.OUT_VERSION, |
| 229 } | 236 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 242 'isolated': { | 249 'isolated': { |
| 243 'input': '123', | 250 'input': '123', |
| 244 'server': 'http://localhost:1', | 251 'server': 'http://localhost:1', |
| 245 'namespace': 'default-gzip', | 252 'namespace': 'default-gzip', |
| 246 }, | 253 }, |
| 247 'task_id': 23, | 254 'task_id': 23, |
| 248 } | 255 } |
| 249 json.dump(data, f) | 256 json.dump(data, f) |
| 250 | 257 |
| 251 out_file = os.path.join(self.root_dir, 'work', 'task_runner_out.json') | 258 out_file = os.path.join(self.root_dir, 'work', 'task_runner_out.json') |
| 252 task_runner.load_and_run(manifest, server, 3600., time.time(), out_file, 1) | 259 task_runner.load_and_run( |
| 260 manifest, server, None, 3600., time.time(), out_file, 1) |
| 253 expected = { | 261 expected = { |
| 254 u'exit_code': 0, | 262 u'exit_code': 0, |
| 255 u'hard_timeout': False, | 263 u'hard_timeout': False, |
| 256 u'io_timeout': False, | 264 u'io_timeout': False, |
| 257 u'must_signal_internal_failure': None, | 265 u'must_signal_internal_failure': None, |
| 258 u'version': task_runner.OUT_VERSION, | 266 u'version': task_runner.OUT_VERSION, |
| 259 } | 267 } |
| 260 with open(out_file, 'rb') as f: | 268 with open(out_file, 'rb') as f: |
| 261 self.assertEqual(expected, json.load(f)) | 269 self.assertEqual(expected, json.load(f)) |
| 262 | 270 |
| 263 def test_run_command_raw(self): | 271 def test_run_command_raw(self): |
| 264 # This runs the command for real. | 272 # This runs the command for real. |
| 265 self.requests(cost_usd=1, exit_code=0) | 273 self.requests(cost_usd=1, exit_code=0) |
| 266 task_details = self.get_task_details('print(\'hi\')') | 274 task_details = self.get_task_details('print(\'hi\')') |
| 267 expected = { | 275 expected = { |
| 268 u'exit_code': 0, | 276 u'exit_code': 0, |
| 269 u'hard_timeout': False, | 277 u'hard_timeout': False, |
| 270 u'io_timeout': False, | 278 u'io_timeout': False, |
| 271 u'must_signal_internal_failure': None, | 279 u'must_signal_internal_failure': None, |
| 272 u'version': task_runner.OUT_VERSION, | 280 u'version': task_runner.OUT_VERSION, |
| 273 } | 281 } |
| 274 self.assertEqual(expected, self._run_command(task_details)) | 282 self.assertEqual(expected, self._run_command(task_details)) |
| 275 | 283 |
| 284 def test_run_command_raw_with_auth(self): |
| 285 headers_file = os.path.join(self.root_dir, 'header.json') |
| 286 with open(headers_file, 'wb') as f: |
| 287 json.dump({'A': 'a'}, f) |
| 288 |
| 289 # This runs the command for real. |
| 290 self.requests(cost_usd=1, exit_code=0, auth_headers={'A': 'a'}) |
| 291 task_details = self.get_task_details('print(\'hi\')') |
| 292 expected = { |
| 293 u'exit_code': 0, |
| 294 u'hard_timeout': False, |
| 295 u'io_timeout': False, |
| 296 u'must_signal_internal_failure': None, |
| 297 u'version': task_runner.OUT_VERSION, |
| 298 } |
| 299 self.assertEqual( |
| 300 expected, self._run_command( |
| 301 task_details, auth_headers_file=headers_file)) |
| 302 |
| 276 def test_run_command_isolated(self): | 303 def test_run_command_isolated(self): |
| 277 # This runs the command for real. | 304 # This runs the command for real. |
| 278 self.requests( | 305 self.requests( |
| 279 cost_usd=1, exit_code=0, | 306 cost_usd=1, exit_code=0, |
| 280 outputs_ref={ | 307 outputs_ref={ |
| 281 u'isolated': u'123', | 308 u'isolated': u'123', |
| 282 u'isolatedserver': u'http://localhost:1', | 309 u'isolatedserver': u'http://localhost:1', |
| 283 u'namespace': u'default-gzip', | 310 u'namespace': u'default-gzip', |
| 284 }) | 311 }) |
| 285 task_details = self.get_task_details(isolated={ | 312 task_details = self.get_task_details(isolated={ |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 409 'cost_usd': 10., | 436 'cost_usd': 10., |
| 410 'duration': 0., | 437 'duration': 0., |
| 411 'exit_code': 0, | 438 'exit_code': 0, |
| 412 'hard_timeout': False, | 439 'hard_timeout': False, |
| 413 'id': 'localhost', | 440 'id': 'localhost', |
| 414 'io_timeout': False, | 441 'io_timeout': False, |
| 415 'output': base64.b64encode('hi!\n'), | 442 'output': base64.b64encode('hi!\n'), |
| 416 'output_chunk_start': 100002*4, | 443 'output_chunk_start': 100002*4, |
| 417 'task_id': 23, | 444 'task_id': 23, |
| 418 }, | 445 }, |
| 446 'follow_redirects': False, |
| 447 'headers': {}, |
| 419 }, | 448 }, |
| 420 kwargs) | 449 kwargs) |
| 421 | 450 |
| 422 requests = [ | 451 requests = [ |
| 423 ( | 452 ( |
| 424 'https://localhost:1/swarming/api/v1/bot/task_update/23', | 453 'https://localhost:1/swarming/api/v1/bot/task_update/23', |
| 425 { | 454 { |
| 426 'data': { | 455 'data': { |
| 427 'cost_usd': 10., | 456 'cost_usd': 10., |
| 428 'id': 'localhost', | 457 'id': 'localhost', |
| 429 'task_id': 23, | 458 'task_id': 23, |
| 430 }, | 459 }, |
| 460 'follow_redirects': False, |
| 461 'headers': {}, |
| 431 }, | 462 }, |
| 432 {'ok': True}, | 463 {'ok': True}, |
| 433 ), | 464 ), |
| 434 ( | 465 ( |
| 435 'https://localhost:1/swarming/api/v1/bot/task_update/23', | 466 'https://localhost:1/swarming/api/v1/bot/task_update/23', |
| 436 { | 467 { |
| 437 'data': { | 468 'data': { |
| 438 'cost_usd': 10., | 469 'cost_usd': 10., |
| 439 'id': 'localhost', | 470 'id': 'localhost', |
| 440 'output': base64.b64encode('hi!\n' * 100002), | 471 'output': base64.b64encode('hi!\n' * 100002), |
| 441 'output_chunk_start': 0, | 472 'output_chunk_start': 0, |
| 442 'task_id': 23, | 473 'task_id': 23, |
| 443 }, | 474 }, |
| 475 'follow_redirects': False, |
| 476 'headers': {}, |
| 444 }, | 477 }, |
| 445 {'ok': True}, | 478 {'ok': True}, |
| 446 ), | 479 ), |
| 447 ( | 480 ( |
| 448 'https://localhost:1/swarming/api/v1/bot/task_update/23', | 481 'https://localhost:1/swarming/api/v1/bot/task_update/23', |
| 449 check_final, | 482 check_final, |
| 450 {'ok': True}, | 483 {'ok': True}, |
| 451 ), | 484 ), |
| 452 ] | 485 ] |
| 453 self.expected_requests(requests) | 486 self.expected_requests(requests) |
| (...skipping 13 matching lines...) Expand all Loading... |
| 467 u'exit_code': 0, | 500 u'exit_code': 0, |
| 468 u'hard_timeout': False, | 501 u'hard_timeout': False, |
| 469 u'io_timeout': False, | 502 u'io_timeout': False, |
| 470 u'must_signal_internal_failure': None, | 503 u'must_signal_internal_failure': None, |
| 471 u'version': task_runner.OUT_VERSION, | 504 u'version': task_runner.OUT_VERSION, |
| 472 } | 505 } |
| 473 self.assertEqual(expected, self._run_command(task_details)) | 506 self.assertEqual(expected, self._run_command(task_details)) |
| 474 | 507 |
| 475 def test_main(self): | 508 def test_main(self): |
| 476 def load_and_run( | 509 def load_and_run( |
| 477 manifest, swarming_server, cost_usd_hour, start, json_file, | 510 manifest, swarming_server, auth_headers_file, cost_usd_hour, start, |
| 478 min_free_space): | 511 json_file, min_free_space): |
| 479 self.assertEqual('foo', manifest) | 512 self.assertEqual('foo', manifest) |
| 480 self.assertEqual('http://localhost', swarming_server) | 513 self.assertEqual('http://localhost', swarming_server) |
| 514 self.assertEqual(None, auth_headers_file) |
| 481 self.assertEqual(3600., cost_usd_hour) | 515 self.assertEqual(3600., cost_usd_hour) |
| 482 self.assertEqual(time.time(), start) | 516 self.assertEqual(time.time(), start) |
| 483 self.assertEqual('task_summary.json', json_file) | 517 self.assertEqual('task_summary.json', json_file) |
| 484 self.assertEqual(1, min_free_space) | 518 self.assertEqual(1, min_free_space) |
| 485 | 519 |
| 486 self.mock(task_runner, 'load_and_run', load_and_run) | 520 self.mock(task_runner, 'load_and_run', load_and_run) |
| 487 cmd = [ | 521 cmd = [ |
| 488 '--swarming-server', 'http://localhost', | 522 '--swarming-server', 'http://localhost', |
| 489 '--in-file', 'foo', | 523 '--in-file', 'foo', |
| 490 '--out-file', 'task_summary.json', | 524 '--out-file', 'task_summary.json', |
| 491 '--cost-usd-hour', '3600', | 525 '--cost-usd-hour', '3600', |
| 492 '--start', str(time.time()), | 526 '--start', str(time.time()), |
| 493 '--min-free-space', '1', | 527 '--min-free-space', '1', |
| 494 ] | 528 ] |
| 495 self.assertEqual(0, task_runner.main(cmd)) | 529 self.assertEqual(0, task_runner.main(cmd)) |
| 496 | 530 |
| 497 def test_main_reboot(self): | 531 def test_main_reboot(self): |
| 498 def load_and_run( | 532 def load_and_run( |
| 499 manifest, swarming_server, cost_usd_hour, start, json_file, | 533 manifest, swarming_server, auth_headers_file, cost_usd_hour, start, |
| 500 min_free_space): | 534 json_file, min_free_space): |
| 501 self.assertEqual('foo', manifest) | 535 self.assertEqual('foo', manifest) |
| 502 self.assertEqual('http://localhost', swarming_server) | 536 self.assertEqual('http://localhost', swarming_server) |
| 537 self.assertEqual(None, auth_headers_file) |
| 503 self.assertEqual(3600., cost_usd_hour) | 538 self.assertEqual(3600., cost_usd_hour) |
| 504 self.assertEqual(time.time(), start) | 539 self.assertEqual(time.time(), start) |
| 505 self.assertEqual('task_summary.json', json_file) | 540 self.assertEqual('task_summary.json', json_file) |
| 506 self.assertEqual(1, min_free_space) | 541 self.assertEqual(1, min_free_space) |
| 507 | 542 |
| 508 self.mock(task_runner, 'load_and_run', load_and_run) | 543 self.mock(task_runner, 'load_and_run', load_and_run) |
| 509 cmd = [ | 544 cmd = [ |
| 510 '--swarming-server', 'http://localhost', | 545 '--swarming-server', 'http://localhost', |
| 511 '--in-file', 'foo', | 546 '--in-file', 'foo', |
| 512 '--out-file', 'task_summary.json', | 547 '--out-file', 'task_summary.json', |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 561 ' except IOError:\n' | 596 ' except IOError:\n' |
| 562 ' pass;\n' | 597 ' pass;\n' |
| 563 'print(\'bye\');\n' | 598 'print(\'bye\');\n' |
| 564 'time.sleep(100)') % ( | 599 'time.sleep(100)') % ( |
| 565 'signal.SIGBREAK' if sys.platform == 'win32' else 'signal.SIGTERM') | 600 'signal.SIGBREAK' if sys.platform == 'win32' else 'signal.SIGTERM') |
| 566 | 601 |
| 567 SCRIPT_HANG = 'import time; print(\'hi\'); time.sleep(100)' | 602 SCRIPT_HANG = 'import time; print(\'hi\'); time.sleep(100)' |
| 568 | 603 |
| 569 def get_check_final( | 604 def get_check_final( |
| 570 self, hard_timeout=False, io_timeout=False, exit_code=None, | 605 self, hard_timeout=False, io_timeout=False, exit_code=None, |
| 571 output_re='^hi\n$'): | 606 output_re='^hi\n$', auth_headers=None): |
| 572 def check_final(kwargs): | 607 def check_final(kwargs): |
| 573 kwargs['data'].pop('bot_overhead', None) | 608 kwargs['data'].pop('bot_overhead', None) |
| 574 if hard_timeout or io_timeout: | 609 if hard_timeout or io_timeout: |
| 575 self.assertLess(self.SHORT_TIME_OUT, kwargs['data'].pop('cost_usd')) | 610 self.assertLess(self.SHORT_TIME_OUT, kwargs['data'].pop('cost_usd')) |
| 576 self.assertLess(self.SHORT_TIME_OUT, kwargs['data'].pop('duration')) | 611 self.assertLess(self.SHORT_TIME_OUT, kwargs['data'].pop('duration')) |
| 577 else: | 612 else: |
| 578 self.assertLess(0., kwargs['data'].pop('cost_usd')) | 613 self.assertLess(0., kwargs['data'].pop('cost_usd')) |
| 579 self.assertLess(0., kwargs['data'].pop('duration')) | 614 self.assertLess(0., kwargs['data'].pop('duration')) |
| 580 | 615 |
| 581 output = '' | 616 output = '' |
| 582 if 'output' in kwargs['data']: | 617 if 'output' in kwargs['data']: |
| 583 output = base64.b64decode(kwargs['data'].pop('output')) | 618 output = base64.b64decode(kwargs['data'].pop('output')) |
| 584 self.assertTrue(re.match(output_re, output)) | 619 self.assertTrue(re.match(output_re, output)) |
| 585 | 620 |
| 586 self.assertEqual( | 621 self.assertEqual( |
| 587 { | 622 { |
| 588 'data': { | 623 'data': { |
| 589 'exit_code': exit_code, | 624 'exit_code': exit_code, |
| 590 'hard_timeout': hard_timeout, | 625 'hard_timeout': hard_timeout, |
| 591 'id': 'localhost', | 626 'id': 'localhost', |
| 592 'io_timeout': io_timeout, | 627 'io_timeout': io_timeout, |
| 593 'output_chunk_start': 0, | 628 'output_chunk_start': 0, |
| 594 'task_id': 23, | 629 'task_id': 23, |
| 595 }, | 630 }, |
| 631 'follow_redirects': False, |
| 632 'headers': auth_headers or {}, |
| 596 }, | 633 }, |
| 597 kwargs) | 634 kwargs) |
| 598 return check_final | 635 return check_final |
| 599 | 636 |
| 600 def _load_and_run(self, manifest): | 637 def _load_and_run(self, manifest): |
| 601 # Dot not mock time since this test class is testing timeouts. | 638 # Dot not mock time since this test class is testing timeouts. |
| 602 server = 'https://localhost:1' | 639 server = 'https://localhost:1' |
| 603 in_file = os.path.join(self.work_dir, 'task_runner_in.json') | 640 in_file = os.path.join(self.work_dir, 'task_runner_in.json') |
| 604 with open(in_file, 'wb') as f: | 641 with open(in_file, 'wb') as f: |
| 605 json.dump(manifest, f) | 642 json.dump(manifest, f) |
| 606 out_file = os.path.join(self.work_dir, 'task_runner_out.json') | 643 out_file = os.path.join(self.work_dir, 'task_runner_out.json') |
| 607 task_runner.load_and_run(in_file, server, 3600., time.time(), out_file, 1) | 644 task_runner.load_and_run( |
| 645 in_file, server, None, 3600., time.time(), out_file, 1) |
| 608 with open(out_file, 'rb') as f: | 646 with open(out_file, 'rb') as f: |
| 609 return json.load(f) | 647 return json.load(f) |
| 610 | 648 |
| 611 def _run_command(self, task_details): | 649 def _run_command(self, task_details): |
| 612 # Dot not mock time since this test class is testing timeouts. | 650 # Dot not mock time since this test class is testing timeouts. |
| 613 server = 'https://localhost:1' | 651 server = 'https://localhost:1' |
| 614 return task_runner.run_command( | 652 return task_runner.run_command( |
| 615 server, task_details, self.work_dir, 3600., time.time(), 1) | 653 server, task_details, self.work_dir, None, 3600., time.time(), 1) |
| 616 | 654 |
| 617 def test_hard(self): | 655 def test_hard(self): |
| 618 # Actually 0xc000013a | 656 # Actually 0xc000013a |
| 619 sig = -1073741510 if sys.platform == 'win32' else -signal.SIGTERM | 657 sig = -1073741510 if sys.platform == 'win32' else -signal.SIGTERM |
| 620 self.requests(hard_timeout=True, exit_code=sig) | 658 self.requests(hard_timeout=True, exit_code=sig) |
| 621 task_details = self.get_task_details( | 659 task_details = self.get_task_details( |
| 622 self.SCRIPT_HANG, hard_timeout=self.SHORT_TIME_OUT) | 660 self.SCRIPT_HANG, hard_timeout=self.SHORT_TIME_OUT) |
| 623 expected = { | 661 expected = { |
| 624 u'exit_code': sig, | 662 u'exit_code': sig, |
| 625 u'hard_timeout': True, | 663 u'hard_timeout': True, |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 794 }, | 832 }, |
| 795 u'upload': { | 833 u'upload': { |
| 796 u'items_cold': [], | 834 u'items_cold': [], |
| 797 u'items_hot': [], | 835 u'items_hot': [], |
| 798 }, | 836 }, |
| 799 }, | 837 }, |
| 800 'output': 'hi\n', | 838 'output': 'hi\n', |
| 801 'output_chunk_start': 0, | 839 'output_chunk_start': 0, |
| 802 'task_id': 23, | 840 'task_id': 23, |
| 803 }, | 841 }, |
| 842 'follow_redirects': False, |
| 843 'headers': {}, |
| 804 }, | 844 }, |
| 805 kwargs) | 845 kwargs) |
| 806 requests = [ | 846 requests = [ |
| 807 ( | 847 ( |
| 808 'https://localhost:1/swarming/api/v1/bot/task_update/23', | 848 'https://localhost:1/swarming/api/v1/bot/task_update/23', |
| 809 self.get_check_first(0.), | 849 self.get_check_first(0.), |
| 810 {'ok': True}, | 850 {'ok': True}, |
| 811 ), | 851 ), |
| 812 ( | 852 ( |
| 813 'https://localhost:1/swarming/api/v1/bot/task_update/23', | 853 'https://localhost:1/swarming/api/v1/bot/task_update/23', |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 913 u'items_hot': [], | 953 u'items_hot': [], |
| 914 }, | 954 }, |
| 915 u'upload': { | 955 u'upload': { |
| 916 u'items_cold': [], | 956 u'items_cold': [], |
| 917 u'items_hot': [], | 957 u'items_hot': [], |
| 918 }, | 958 }, |
| 919 }, | 959 }, |
| 920 'output_chunk_start': 0, | 960 'output_chunk_start': 0, |
| 921 'task_id': 23, | 961 'task_id': 23, |
| 922 }, | 962 }, |
| 963 'follow_redirects': False, |
| 964 'headers': {}, |
| 923 }, | 965 }, |
| 924 kwargs) | 966 kwargs) |
| 925 requests = [ | 967 requests = [ |
| 926 ( | 968 ( |
| 927 'https://localhost:1/swarming/api/v1/bot/task_update/23', | 969 'https://localhost:1/swarming/api/v1/bot/task_update/23', |
| 928 self.get_check_first(0.), | 970 self.get_check_first(0.), |
| 929 {'ok': True}, | 971 {'ok': True}, |
| 930 ), | 972 ), |
| 931 ( | 973 ( |
| 932 'https://localhost:1/swarming/api/v1/bot/task_update/23', | 974 'https://localhost:1/swarming/api/v1/bot/task_update/23', |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1096 fix_encoding.fix_encoding() | 1138 fix_encoding.fix_encoding() |
| 1097 if '-v' in sys.argv: | 1139 if '-v' in sys.argv: |
| 1098 unittest.TestCase.maxDiff = None | 1140 unittest.TestCase.maxDiff = None |
| 1099 logging_utils.prepare_logging(None) | 1141 logging_utils.prepare_logging(None) |
| 1100 logging_utils.set_console_level( | 1142 logging_utils.set_console_level( |
| 1101 logging.DEBUG if '-v' in sys.argv else logging.CRITICAL+1) | 1143 logging.DEBUG if '-v' in sys.argv else logging.CRITICAL+1) |
| 1102 # Fix litteral text expectation. | 1144 # Fix litteral text expectation. |
| 1103 os.environ['LANG'] = 'en_US.UTF-8' | 1145 os.environ['LANG'] = 'en_US.UTF-8' |
| 1104 os.environ['LANGUAGE'] = 'en_US.UTF-8' | 1146 os.environ['LANGUAGE'] = 'en_US.UTF-8' |
| 1105 unittest.main() | 1147 unittest.main() |
| OLD | NEW |