OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2013 The LUCI Authors. All rights reserved. | 2 # Copyright 2013 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 import datetime | 6 import datetime |
7 import json | 7 import json |
8 import logging | 8 import logging |
9 import os | 9 import os |
10 import re | 10 import re |
11 import StringIO | 11 import StringIO |
12 import subprocess | 12 import subprocess |
13 import sys | 13 import sys |
14 import tempfile | 14 import tempfile |
15 import threading | 15 import threading |
16 import time | 16 import time |
| 17 import traceback |
17 import unittest | 18 import unittest |
18 | 19 |
19 # net_utils adjusts sys.path. | 20 # net_utils adjusts sys.path. |
20 import net_utils | 21 import net_utils |
21 | 22 |
22 from depot_tools import auto_stub | 23 from depot_tools import auto_stub |
23 | 24 |
24 import auth | 25 import auth |
25 import isolateserver | 26 import isolateserver |
26 import swarming | 27 import swarming |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
95 'command': None, | 96 'command': None, |
96 'dimensions': [ | 97 'dimensions': [ |
97 {'key': 'foo', 'value': 'bar'}, | 98 {'key': 'foo', 'value': 'bar'}, |
98 {'key': 'os', 'value': 'Mac'}, | 99 {'key': 'os', 'value': 'Mac'}, |
99 ], | 100 ], |
100 'env': [], | 101 'env': [], |
101 'execution_timeout_secs': 60, | 102 'execution_timeout_secs': 60, |
102 'extra_args': ['--some-arg', '123'], | 103 'extra_args': ['--some-arg', '123'], |
103 'grace_period_secs': 30, | 104 'grace_period_secs': 30, |
104 'idempotent': False, | 105 'idempotent': False, |
105 'inputs_ref': None, | 106 'inputs_ref': { |
| 107 'isolated': None, |
| 108 'isolatedserver': '', |
| 109 'namespace': 'default-gzip', |
| 110 }, |
106 'io_timeout_secs': 60, | 111 'io_timeout_secs': 60, |
107 'outputs': [], | 112 'outputs': [], |
108 'secret_bytes': None, | 113 'secret_bytes': None, |
109 }, | 114 }, |
110 'tags': ['tag:a', 'tag:b'], | 115 'tags': ['tag:a', 'tag:b'], |
111 'user': 'joe@localhost', | 116 'user': 'joe@localhost', |
112 } | 117 } |
113 out.update(kwargs) | 118 out.update(kwargs) |
114 out['properties'].update(properties or {}) | 119 out['properties'].update(properties or {}) |
115 return out | 120 return out |
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
229 | 234 |
230 def main_safe(self, args): | 235 def main_safe(self, args): |
231 """Bypasses swarming.main()'s exception handling. | 236 """Bypasses swarming.main()'s exception handling. |
232 | 237 |
233 It gets in the way when debugging test failures. | 238 It gets in the way when debugging test failures. |
234 """ | 239 """ |
235 # pylint: disable=bare-except | 240 # pylint: disable=bare-except |
236 try: | 241 try: |
237 return main(args) | 242 return main(args) |
238 except: | 243 except: |
239 logging.exception('Unexpected exception thrown in main') | 244 data = '%s\nSTDOUT:\n%s\nSTDERR:\n%s' % ( |
240 logging.error( | 245 traceback.format_exc(), sys.stdout.getvalue(), sys.stderr.getvalue()) |
241 'STDOUT:\n%s\nSTDERR:\n%s', | 246 self.fail(data) |
242 sys.stdout.getvalue(), sys.stderr.getvalue()) | |
243 self.fail() | |
244 | 247 |
245 | 248 |
246 class NetTestCase(net_utils.TestCase, Common): | 249 class NetTestCase(net_utils.TestCase, Common): |
247 """Base class that defines the url_open mock.""" | 250 """Base class that defines the url_open mock.""" |
248 def setUp(self): | 251 def setUp(self): |
249 net_utils.TestCase.setUp(self) | 252 net_utils.TestCase.setUp(self) |
250 Common.setUp(self) | 253 Common.setUp(self) |
251 self.mock(time, 'sleep', lambda _: None) | 254 self.mock(time, 'sleep', lambda _: None) |
252 self.mock(subprocess, 'call', lambda *_: self.fail()) | 255 self.mock(subprocess, 'call', lambda *_: self.fail()) |
253 self.mock(threading, 'Event', NonBlockingEvent) | 256 self.mock(threading, 'Event', NonBlockingEvent) |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
333 properties=swarming.TaskProperties( | 336 properties=swarming.TaskProperties( |
334 caches=[], | 337 caches=[], |
335 cipd_input=None, | 338 cipd_input=None, |
336 command=['a', 'b'], | 339 command=['a', 'b'], |
337 dimensions={'foo': 'bar', 'os': 'Mac'}, | 340 dimensions={'foo': 'bar', 'os': 'Mac'}, |
338 env={}, | 341 env={}, |
339 execution_timeout_secs=60, | 342 execution_timeout_secs=60, |
340 extra_args=[], | 343 extra_args=[], |
341 grace_period_secs=30, | 344 grace_period_secs=30, |
342 idempotent=False, | 345 idempotent=False, |
343 inputs_ref=None, | 346 inputs_ref={ |
| 347 'isolated': None, |
| 348 'isolatedserver': '', |
| 349 'namespace': 'default-gzip', |
| 350 }, |
344 io_timeout_secs=60, | 351 io_timeout_secs=60, |
345 outputs=[], | 352 outputs=[], |
346 secret_bytes=None), | 353 secret_bytes=None), |
347 service_account_token=None, | 354 service_account_token=None, |
348 tags=['tag:a', 'tag:b'], | 355 tags=['tag:a', 'tag:b'], |
349 user='joe@localhost') | 356 user='joe@localhost') |
350 | 357 |
351 request_1 = swarming.task_request_to_raw_request(task_request, False) | 358 request_1 = swarming.task_request_to_raw_request(task_request, False) |
352 request_1['name'] = u'unit_tests:0:2' | 359 request_1['name'] = u'unit_tests:0:2' |
353 request_1['properties']['env'] = [ | 360 request_1['properties']['env'] = [ |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
404 properties=swarming.TaskProperties( | 411 properties=swarming.TaskProperties( |
405 caches=[], | 412 caches=[], |
406 cipd_input=None, | 413 cipd_input=None, |
407 command=['a', 'b'], | 414 command=['a', 'b'], |
408 dimensions={'foo': 'bar', 'os': 'Mac'}, | 415 dimensions={'foo': 'bar', 'os': 'Mac'}, |
409 env={}, | 416 env={}, |
410 execution_timeout_secs=60, | 417 execution_timeout_secs=60, |
411 extra_args=[], | 418 extra_args=[], |
412 grace_period_secs=30, | 419 grace_period_secs=30, |
413 idempotent=False, | 420 idempotent=False, |
414 inputs_ref=None, | 421 inputs_ref={ |
| 422 'isolated': None, |
| 423 'isolatedserver': '', |
| 424 'namespace': 'default-gzip', |
| 425 }, |
415 io_timeout_secs=60, | 426 io_timeout_secs=60, |
416 outputs=[], | 427 outputs=[], |
417 secret_bytes=None), | 428 secret_bytes=None), |
418 service_account_token=None, | 429 service_account_token=None, |
419 tags=['tag:a', 'tag:b'], | 430 tags=['tag:a', 'tag:b'], |
420 user='joe@localhost') | 431 user='joe@localhost') |
421 | 432 |
422 request = swarming.task_request_to_raw_request(task_request, False) | 433 request = swarming.task_request_to_raw_request(task_request, False) |
423 self.assertEqual('123', request['parent_task_id']) | 434 self.assertEqual('123', request['parent_task_id']) |
424 | 435 |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
467 path='path/to/package', | 478 path='path/to/package', |
468 version='abc123')], | 479 version='abc123')], |
469 server=None), | 480 server=None), |
470 command=['a', 'b'], | 481 command=['a', 'b'], |
471 dimensions={'foo': 'bar', 'os': 'Mac'}, | 482 dimensions={'foo': 'bar', 'os': 'Mac'}, |
472 env={}, | 483 env={}, |
473 execution_timeout_secs=60, | 484 execution_timeout_secs=60, |
474 extra_args=[], | 485 extra_args=[], |
475 grace_period_secs=30, | 486 grace_period_secs=30, |
476 idempotent=False, | 487 idempotent=False, |
477 inputs_ref=None, | 488 inputs_ref={ |
| 489 'isolated': None, |
| 490 'isolatedserver': '', |
| 491 'namespace': 'default-gzip', |
| 492 }, |
478 io_timeout_secs=60, | 493 io_timeout_secs=60, |
479 outputs=[], | 494 outputs=[], |
480 secret_bytes=None), | 495 secret_bytes=None), |
481 service_account_token=None, | 496 service_account_token=None, |
482 tags=['tag:a', 'tag:b'], | 497 tags=['tag:a', 'tag:b'], |
483 user='joe@localhost') | 498 user='joe@localhost') |
484 | 499 |
485 request = swarming.task_request_to_raw_request(task_request, False) | 500 request = swarming.task_request_to_raw_request(task_request, False) |
486 expected = { | 501 expected = { |
487 'client_package': None, | 502 'client_package': None, |
(...skipping 418 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
906 actual = sys.stdout.getvalue() | 921 actual = sys.stdout.getvalue() |
907 self.assertEqual(0, ret, (actual, sys.stderr.getvalue())) | 922 self.assertEqual(0, ret, (actual, sys.stderr.getvalue())) |
908 self._check_output( | 923 self._check_output( |
909 'Triggered task: None/foo=bar\n' | 924 'Triggered task: None/foo=bar\n' |
910 'To collect results, use:\n' | 925 'To collect results, use:\n' |
911 ' swarming.py collect -S https://localhost:1 12300\n' | 926 ' swarming.py collect -S https://localhost:1 12300\n' |
912 'Or visit:\n' | 927 'Or visit:\n' |
913 ' https://localhost:1/user/task/12300\n', | 928 ' https://localhost:1/user/task/12300\n', |
914 '') | 929 '') |
915 | 930 |
| 931 def test_run_raw_cmd_isolated(self): |
| 932 # Minimalist use. |
| 933 request = { |
| 934 'expiration_secs': 21600, |
| 935 'name': u'None/foo=bar/' + FILE_HASH, |
| 936 'parent_task_id': '', |
| 937 'priority': 100, |
| 938 'properties': { |
| 939 'caches': [], |
| 940 'cipd_input': None, |
| 941 'command': ['python', '-c', 'print(\'hi\')'], |
| 942 'dimensions': [ |
| 943 {'key': 'foo', 'value': 'bar'}, |
| 944 ], |
| 945 'env': [], |
| 946 'execution_timeout_secs': 3600, |
| 947 'extra_args': None, |
| 948 'grace_period_secs': 30, |
| 949 'idempotent': False, |
| 950 'inputs_ref': { |
| 951 'isolated': FILE_HASH, |
| 952 'isolatedserver': 'https://localhost:2', |
| 953 'namespace': 'default-gzip', |
| 954 }, |
| 955 'io_timeout_secs': 1200, |
| 956 'outputs': [], |
| 957 'secret_bytes': None, |
| 958 }, |
| 959 'tags': [], |
| 960 'user': None, |
| 961 } |
| 962 result = gen_request_response(request) |
| 963 self.expected_requests( |
| 964 [ |
| 965 ( |
| 966 'https://localhost:1/api/swarming/v1/tasks/new', |
| 967 {'data': request}, |
| 968 result, |
| 969 ), |
| 970 ]) |
| 971 ret = self.main_safe([ |
| 972 'trigger', |
| 973 '--swarming', 'https://localhost:1', |
| 974 '--dimension', 'foo', 'bar', |
| 975 '--raw-cmd', |
| 976 '--isolate-server', 'https://localhost:2', |
| 977 '--isolated', FILE_HASH, |
| 978 '--', |
| 979 'python', |
| 980 '-c', |
| 981 'print(\'hi\')', |
| 982 ]) |
| 983 actual = sys.stdout.getvalue() |
| 984 self.assertEqual(0, ret, (actual, sys.stderr.getvalue())) |
| 985 self._check_output( |
| 986 u'Triggered task: None/foo=bar/' + FILE_HASH + u'\n' |
| 987 u'To collect results, use:\n' |
| 988 u' swarming.py collect -S https://localhost:1 12300\n' |
| 989 u'Or visit:\n' |
| 990 u' https://localhost:1/user/task/12300\n', |
| 991 u'') |
| 992 |
916 def test_run_raw_cmd_with_service_account(self): | 993 def test_run_raw_cmd_with_service_account(self): |
917 # Minimalist use. | 994 # Minimalist use. |
918 request = { | 995 request = { |
919 'expiration_secs': 21600, | 996 'expiration_secs': 21600, |
920 'name': u'None/foo=bar', | 997 'name': u'None/foo=bar', |
921 'parent_task_id': '', | 998 'parent_task_id': '', |
922 'priority': 100, | 999 'priority': 100, |
923 'properties': { | 1000 'properties': { |
924 'caches': [], | 1001 'caches': [], |
925 'cipd_input': None, | 1002 'cipd_input': None, |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
972 '') | 1049 '') |
973 | 1050 |
974 def test_run_isolated_hash(self): | 1051 def test_run_isolated_hash(self): |
975 # pylint: disable=unused-argument | 1052 # pylint: disable=unused-argument |
976 self.mock(swarming, 'now', lambda: 123456) | 1053 self.mock(swarming, 'now', lambda: 123456) |
977 | 1054 |
978 request = gen_request_data( | 1055 request = gen_request_data( |
979 properties={ | 1056 properties={ |
980 'command': None, | 1057 'command': None, |
981 'inputs_ref': { | 1058 'inputs_ref': { |
982 'isolated': u'1111111111111111111111111111111111111111', | 1059 'isolated': FILE_HASH, |
983 'isolatedserver': 'https://localhost:2', | 1060 'isolatedserver': 'https://localhost:2', |
984 'namespace': 'default-gzip', | 1061 'namespace': 'default-gzip', |
985 }, | 1062 }, |
986 'secret_bytes': None, | 1063 'secret_bytes': None, |
987 }) | 1064 }) |
988 result = gen_request_response(request) | 1065 result = gen_request_response(request) |
989 self.expected_requests( | 1066 self.expected_requests( |
990 [ | 1067 [ |
991 ( | 1068 ( |
992 'https://localhost:1/api/swarming/v1/tasks/new', | 1069 'https://localhost:1/api/swarming/v1/tasks/new', |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1147 'client_package': None, | 1224 'client_package': None, |
1148 'packages': [{ | 1225 'packages': [{ |
1149 'package_name': 'super/awesome/pkg', | 1226 'package_name': 'super/awesome/pkg', |
1150 'path': 'path/to/pkg', | 1227 'path': 'path/to/pkg', |
1151 'version': 'version:42', | 1228 'version': 'version:42', |
1152 }], | 1229 }], |
1153 'server': None, | 1230 'server': None, |
1154 }, | 1231 }, |
1155 'command': None, | 1232 'command': None, |
1156 'inputs_ref': { | 1233 'inputs_ref': { |
1157 'isolated': u'1111111111111111111111111111111111111111', | 1234 'isolated': FILE_HASH, |
1158 'isolatedserver': 'https://localhost:2', | 1235 'isolatedserver': 'https://localhost:2', |
1159 'namespace': 'default-gzip', | 1236 'namespace': 'default-gzip', |
1160 }, | 1237 }, |
1161 'secret_bytes': None, | 1238 'secret_bytes': None, |
1162 }) | 1239 }) |
1163 result = gen_request_response(request) | 1240 result = gen_request_response(request) |
1164 self.expected_requests( | 1241 self.expected_requests( |
1165 [ | 1242 [ |
1166 ( | 1243 ( |
1167 'https://localhost:1/api/swarming/v1/tasks/new', | 1244 'https://localhost:1/api/swarming/v1/tasks/new', |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1205 main([ | 1282 main([ |
1206 'trigger', '--swarming', 'https://host', | 1283 'trigger', '--swarming', 'https://host', |
1207 '--isolate-server', 'https://host', '-T', 'foo', | 1284 '--isolate-server', 'https://host', '-T', 'foo', |
1208 '-d', 'os', 'amgia', | 1285 '-d', 'os', 'amgia', |
1209 ]) | 1286 ]) |
1210 self._check_output( | 1287 self._check_output( |
1211 '', | 1288 '', |
1212 'Usage: swarming.py trigger [options] (hash|isolated) ' | 1289 'Usage: swarming.py trigger [options] (hash|isolated) ' |
1213 '[-- extra_args|raw command]\n' | 1290 '[-- extra_args|raw command]\n' |
1214 '\n' | 1291 '\n' |
1215 'swarming.py: error: Use --isolated, --raw-cmd or \'--\' to pass ' | 1292 'swarming.py: error: Specify at least one of --raw-cmd or --isolated ' |
1216 'arguments to the called process.\n') | 1293 'or both\n') |
1217 | 1294 |
1218 def test_trigger_no_env_vars(self): | 1295 def test_trigger_no_env_vars(self): |
1219 with self.assertRaises(SystemExit): | 1296 with self.assertRaises(SystemExit): |
1220 main(['trigger']) | 1297 main(['trigger']) |
1221 self._check_output( | 1298 self._check_output( |
1222 '', | 1299 '', |
1223 'Usage: swarming.py trigger [options] (hash|isolated) ' | 1300 'Usage: swarming.py trigger [options] (hash|isolated) ' |
1224 '[-- extra_args|raw command]' | 1301 '[-- extra_args|raw command]' |
1225 '\n\n' | 1302 '\n\n' |
1226 'swarming.py: error: --swarming is required.' | 1303 'swarming.py: error: --swarming is required.' |
(...skipping 13 matching lines...) Expand all Loading... |
1240 | 1317 |
1241 def test_trigger_no_isolate_server(self): | 1318 def test_trigger_no_isolate_server(self): |
1242 with self.assertRaises(SystemExit): | 1319 with self.assertRaises(SystemExit): |
1243 with test_utils.EnvVars({'SWARMING_SERVER': 'https://host'}): | 1320 with test_utils.EnvVars({'SWARMING_SERVER': 'https://host'}): |
1244 main(['trigger', 'foo.isolated', '-d', 'os', 'amiga']) | 1321 main(['trigger', 'foo.isolated', '-d', 'os', 'amiga']) |
1245 self._check_output( | 1322 self._check_output( |
1246 '', | 1323 '', |
1247 'Usage: swarming.py trigger [options] (hash|isolated) ' | 1324 'Usage: swarming.py trigger [options] (hash|isolated) ' |
1248 '[-- extra_args|raw command]' | 1325 '[-- extra_args|raw command]' |
1249 '\n\n' | 1326 '\n\n' |
1250 'swarming.py: error: --isolate-server is required.' | 1327 'swarming.py: error: Specify at least one of --raw-cmd or --isolated ' |
1251 '\n') | 1328 'or both\n') |
1252 | 1329 |
1253 def test_trigger_no_dimension(self): | 1330 def test_trigger_no_dimension(self): |
1254 with self.assertRaises(SystemExit): | 1331 with self.assertRaises(SystemExit): |
1255 main([ | 1332 main([ |
1256 'trigger', '--swarming', 'https://host', '--raw-cmd', '--', 'foo', | 1333 'trigger', '--swarming', 'https://host', '--raw-cmd', '--', 'foo', |
1257 ]) | 1334 ]) |
1258 self._check_output( | 1335 self._check_output( |
1259 '', | 1336 '', |
1260 'Usage: swarming.py trigger [options] (hash|isolated) ' | 1337 'Usage: swarming.py trigger [options] (hash|isolated) ' |
1261 '[-- extra_args|raw command]' | 1338 '[-- extra_args|raw command]' |
(...skipping 21 matching lines...) Expand all Loading... |
1283 'dimensions': [ | 1360 'dimensions': [ |
1284 {'key': 'foo', 'value': 'bar'}, | 1361 {'key': 'foo', 'value': 'bar'}, |
1285 {'key': 'os', 'value': 'Mac'}, | 1362 {'key': 'os', 'value': 'Mac'}, |
1286 ], | 1363 ], |
1287 'env': [], | 1364 'env': [], |
1288 'execution_timeout_secs': 60, | 1365 'execution_timeout_secs': 60, |
1289 'extra_args': ['--some-arg', '123'], | 1366 'extra_args': ['--some-arg', '123'], |
1290 'grace_period_secs': 30, | 1367 'grace_period_secs': 30, |
1291 'idempotent': True, | 1368 'idempotent': True, |
1292 'inputs_ref': { | 1369 'inputs_ref': { |
1293 'isolated': '1'*40, | 1370 'isolated': FILE_HASH, |
1294 'isolatedserver': 'https://localhost:2', | 1371 'isolatedserver': 'https://localhost:2', |
1295 'namespace': 'default-gzip', | 1372 'namespace': 'default-gzip', |
1296 }, | 1373 }, |
1297 'io_timeout_secs': 60, | 1374 'io_timeout_secs': 60, |
1298 'secret_bytes': None, | 1375 'secret_bytes': None, |
1299 }, | 1376 }, |
1300 'tags': ['tag:a', 'tag:b'], | 1377 'tags': ['tag:a', 'tag:b'], |
1301 'user': 'joe@localhost', | 1378 'user': 'joe@localhost', |
1302 }, | 1379 }, |
1303 } | 1380 } |
(...skipping 322 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1626 | 1703 |
1627 if __name__ == '__main__': | 1704 if __name__ == '__main__': |
1628 fix_encoding.fix_encoding() | 1705 fix_encoding.fix_encoding() |
1629 logging.basicConfig( | 1706 logging.basicConfig( |
1630 level=logging.DEBUG if '-v' in sys.argv else logging.CRITICAL) | 1707 level=logging.DEBUG if '-v' in sys.argv else logging.CRITICAL) |
1631 if '-v' in sys.argv: | 1708 if '-v' in sys.argv: |
1632 unittest.TestCase.maxDiff = None | 1709 unittest.TestCase.maxDiff = None |
1633 for e in ('ISOLATE_SERVER', 'SWARMING_TASK_ID', 'SWARMING_SERVER'): | 1710 for e in ('ISOLATE_SERVER', 'SWARMING_TASK_ID', 'SWARMING_SERVER'): |
1634 os.environ.pop(e, None) | 1711 os.environ.pop(e, None) |
1635 unittest.main() | 1712 unittest.main() |
OLD | NEW |