blob: 1bf9e00c7cd086ac8df6ca8ca6390f87efa6d970 [file] [log] [blame]
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +00001#!/usr/bin/env python
Marc-Antoine Ruel8add1242013-11-05 17:28:27 -05002# Copyright 2012 The Swarming Authors. All rights reserved.
Marc-Antoine Ruele98b1122013-11-05 20:27:57 -05003# Use of this source code is governed under the Apache License, Version 2.0 that
4# can be found in the LICENSE file.
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +00005
maruel@chromium.org0cd0b182012-10-22 13:34:15 +00006"""Reads a .isolated, creates a tree of hardlinks and runs the test.
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +00007
Marc-Antoine Ruel2283ad12014-02-09 11:14:57 -05008To improve performance, it keeps a local cache. The local cache can safely be
9deleted.
10
11Any ${ISOLATED_OUTDIR} on the command line will be replaced by the location of a
12temporary directory upon execution of the command specified in the .isolated
13file. All content written to this directory will be uploaded upon termination
14and the .isolated file describing this directory will be printed to stdout.
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +000015"""
16
maruel6be7f9e2015-10-01 12:25:30 -070017__version__ = '0.5.3'
maruel@chromium.orgdedbf492013-09-12 20:42:11 +000018
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +000019import logging
20import optparse
21import os
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +000022import sys
23import tempfile
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +000024
vadimsh@chromium.orga4326472013-08-24 02:05:41 +000025from third_party.depot_tools import fix_encoding
26
Vadim Shtayura6b555c12014-07-23 16:22:18 -070027from utils import file_path
Marc-Antoine Ruelf74cffe2015-07-15 15:21:34 -040028from utils import logging_utils
Marc-Antoine Ruelcfb60852014-07-02 15:22:00 -040029from utils import on_error
Marc-Antoine Ruelc44f5722015-01-08 16:10:01 -050030from utils import subprocess42
vadimsh@chromium.orga4326472013-08-24 02:05:41 +000031from utils import tools
vadimsh@chromium.org3e97deb2013-08-24 00:56:44 +000032from utils import zip_package
vadimsh@chromium.org8b9d56b2013-08-21 22:24:35 +000033
Vadim Shtayurae34e13a2014-02-02 11:23:26 -080034import auth
Marc-Antoine Ruel8bee66d2014-08-28 19:02:07 -040035import isolated_format
maruel@chromium.orgdedbf492013-09-12 20:42:11 +000036import isolateserver
maruel@chromium.orgdedbf492013-09-12 20:42:11 +000037
vadimsh@chromium.orga4326472013-08-24 02:05:41 +000038
vadimsh@chromium.org85071062013-08-21 23:37:45 +000039# Absolute path to this file (can be None if running from zip on Mac).
40THIS_FILE_PATH = os.path.abspath(__file__) if __file__ else None
vadimsh@chromium.org8b9d56b2013-08-21 22:24:35 +000041
42# Directory that contains this file (might be inside zip package).
vadimsh@chromium.org85071062013-08-21 23:37:45 +000043BASE_DIR = os.path.dirname(THIS_FILE_PATH) if __file__ else None
vadimsh@chromium.org8b9d56b2013-08-21 22:24:35 +000044
45# Directory that contains currently running script file.
maruel@chromium.org814d23f2013-10-01 19:08:00 +000046if zip_package.get_main_script_path():
47 MAIN_DIR = os.path.dirname(
48 os.path.abspath(zip_package.get_main_script_path()))
49else:
50 # This happens when 'import run_isolated' is executed at the python
51 # interactive prompt, in that case __file__ is undefined.
52 MAIN_DIR = None
vadimsh@chromium.org8b9d56b2013-08-21 22:24:35 +000053
csharp@chromium.orgff2a4662012-11-21 20:49:32 +000054# The name of the log file to use.
55RUN_ISOLATED_LOG_FILE = 'run_isolated.log'
56
csharp@chromium.orge217f302012-11-22 16:51:53 +000057# The name of the log to use for the run_test_cases.py command
vadimsh@chromium.org8b9d56b2013-08-21 22:24:35 +000058RUN_TEST_CASES_LOG = 'run_test_cases.log'
csharp@chromium.orge217f302012-11-22 16:51:53 +000059
vadimsh@chromium.org87d63262013-04-04 19:34:21 +000060
vadimsh@chromium.org8b9d56b2013-08-21 22:24:35 +000061def get_as_zip_package(executable=True):
62 """Returns ZipPackage with this module and all its dependencies.
63
64 If |executable| is True will store run_isolated.py as __main__.py so that
65 zip package is directly executable be python.
66 """
67 # Building a zip package when running from another zip package is
68 # unsupported and probably unneeded.
69 assert not zip_package.is_zipped_module(sys.modules[__name__])
vadimsh@chromium.org85071062013-08-21 23:37:45 +000070 assert THIS_FILE_PATH
71 assert BASE_DIR
vadimsh@chromium.org8b9d56b2013-08-21 22:24:35 +000072 package = zip_package.ZipPackage(root=BASE_DIR)
73 package.add_python_file(THIS_FILE_PATH, '__main__.py' if executable else None)
Marc-Antoine Ruel8bee66d2014-08-28 19:02:07 -040074 package.add_python_file(os.path.join(BASE_DIR, 'isolated_format.py'))
maruel@chromium.orgdedbf492013-09-12 20:42:11 +000075 package.add_python_file(os.path.join(BASE_DIR, 'isolateserver.py'))
Vadim Shtayurae34e13a2014-02-02 11:23:26 -080076 package.add_python_file(os.path.join(BASE_DIR, 'auth.py'))
vadimsh@chromium.org8b9d56b2013-08-21 22:24:35 +000077 package.add_directory(os.path.join(BASE_DIR, 'third_party'))
78 package.add_directory(os.path.join(BASE_DIR, 'utils'))
79 return package
80
81
Vadim Shtayuracb0b7432015-07-31 13:26:50 -070082def make_temp_dir(prefix, root_dir=None):
83 """Returns a temporary directory.
84
85 If root_dir is given and /tmp is on same file system as root_dir, uses /tmp.
86 Otherwise makes a new temp directory under root_dir.
87 """
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +000088 base_temp_dir = None
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -040089 if (root_dir and
90 not file_path.is_same_filesystem(root_dir, tempfile.gettempdir())):
Paweł Hajdan, Jrf7d58722015-04-27 14:54:42 +020091 base_temp_dir = root_dir
marueleb5fbee2015-09-17 13:01:36 -070092 return unicode(tempfile.mkdtemp(prefix=prefix, dir=base_temp_dir))
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +000093
94
Marc-Antoine Ruel7124e392014-01-09 11:49:21 -050095def change_tree_read_only(rootdir, read_only):
96 """Changes the tree read-only bits according to the read_only specification.
97
98 The flag can be 0, 1 or 2, which will affect the possibility to modify files
99 and create or delete files.
100 """
101 if read_only == 2:
102 # Files and directories (except on Windows) are marked read only. This
103 # inhibits modifying, creating or deleting files in the test directory,
104 # except on Windows where creating and deleting files is still possible.
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400105 file_path.make_tree_read_only(rootdir)
Marc-Antoine Ruel7124e392014-01-09 11:49:21 -0500106 elif read_only == 1:
107 # Files are marked read only but not the directories. This inhibits
108 # modifying files but creating or deleting files is still possible.
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400109 file_path.make_tree_files_read_only(rootdir)
Marc-Antoine Ruel7124e392014-01-09 11:49:21 -0500110 elif read_only in (0, None):
Marc-Antoine Ruelf1d827c2014-11-24 15:22:25 -0500111 # Anything can be modified.
Marc-Antoine Ruel7124e392014-01-09 11:49:21 -0500112 # TODO(maruel): This is currently dangerous as long as DiskCache.touch()
113 # is not yet changed to verify the hash of the content of the files it is
114 # looking at, so that if a test modifies an input file, the file must be
115 # deleted.
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400116 file_path.make_tree_writeable(rootdir)
Marc-Antoine Ruel7124e392014-01-09 11:49:21 -0500117 else:
118 raise ValueError(
119 'change_tree_read_only(%s, %s): Unknown flag %s' %
120 (rootdir, read_only, read_only))
121
122
Marc-Antoine Ruel2283ad12014-02-09 11:14:57 -0500123def process_command(command, out_dir):
124 """Replaces isolated specific variables in a command line."""
maruela9cfd6f2015-09-15 11:03:15 -0700125 def fix(arg):
Vadim Shtayura51aba362014-05-14 15:39:23 -0700126 if '${ISOLATED_OUTDIR}' in arg:
maruela9cfd6f2015-09-15 11:03:15 -0700127 return arg.replace('${ISOLATED_OUTDIR}', out_dir).replace('/', os.sep)
128 return arg
129
130 return [fix(arg) for arg in command]
131
132
maruel6be7f9e2015-10-01 12:25:30 -0700133def run_command(command, cwd, tmp_dir, hard_timeout, grace_period):
134 """Runs the command.
135
136 Returns:
137 tuple(process exit code, bool if had a hard timeout)
138 """
maruela9cfd6f2015-09-15 11:03:15 -0700139 logging.info('run_command(%s, %s)' % (command, cwd))
140 sys.stdout.flush()
marueleb5fbee2015-09-17 13:01:36 -0700141
142 env = os.environ.copy()
143 if sys.platform == 'darwin':
144 env['TMPDIR'] = tmp_dir.encode('ascii')
145 elif sys.platform == 'win32':
maruelf71d1a32015-09-18 11:27:35 -0700146 # Temporarily disable this behavior on Windows while investigating
147 # https://crbug.com/533552.
148 # env['TEMP'] = tmp_dir.encode('ascii')
149 pass
marueleb5fbee2015-09-17 13:01:36 -0700150 else:
151 env['TMP'] = tmp_dir.encode('ascii')
maruel6be7f9e2015-10-01 12:25:30 -0700152 exit_code = None
153 had_hard_timeout = False
maruela9cfd6f2015-09-15 11:03:15 -0700154 with tools.Profiler('RunTest'):
maruel6be7f9e2015-10-01 12:25:30 -0700155 proc = None
156 had_signal = []
maruela9cfd6f2015-09-15 11:03:15 -0700157 try:
maruel6be7f9e2015-10-01 12:25:30 -0700158 # TODO(maruel): This code is imperfect. It doesn't handle well signals
159 # during the download phase and there's short windows were things can go
160 # wrong.
161 def handler(signum, _frame):
162 if proc and not had_signal:
163 logging.info('Received signal %d', signum)
164 had_signal.append(True)
165 raise subprocess42.TimeoutExpired()
166
167 proc = subprocess42.Popen(command, cwd=cwd, env=env, detached=True)
168 with subprocess42.set_signal_handler(subprocess42.STOP_SIGNALS, handler):
169 try:
170 exit_code = proc.wait(hard_timeout or None)
171 except subprocess42.TimeoutExpired:
172 if not had_signal:
173 logging.warning('Hard timeout')
174 had_hard_timeout = True
175 logging.warning('Sending SIGTERM')
176 proc.terminate()
177
178 # Ignore signals in grace period. Forcibly give the grace period to the
179 # child process.
180 if exit_code is None:
181 ignore = lambda *_: None
182 with subprocess42.set_signal_handler(subprocess42.STOP_SIGNALS, ignore):
183 try:
184 exit_code = proc.wait(grace_period or None)
185 except subprocess42.TimeoutExpired:
186 # Now kill for real. The user can distinguish between the
187 # following states:
188 # - signal but process exited within grace period,
189 # hard_timed_out will be set but the process exit code will be
190 # script provided.
191 # - processed exited late, exit code will be -9 on posix.
192 logging.warning('Grace exhausted; sending SIGKILL')
193 proc.kill()
194 logging.info('Waiting for proces exit')
195 exit_code = proc.wait()
maruela9cfd6f2015-09-15 11:03:15 -0700196 except OSError:
197 # This is not considered to be an internal error. The executable simply
198 # does not exit.
199 exit_code = 1
200 logging.info(
201 'Command finished with exit code %d (%s)',
202 exit_code, hex(0xffffffff & exit_code))
maruel6be7f9e2015-10-01 12:25:30 -0700203 return exit_code, had_hard_timeout
maruela9cfd6f2015-09-15 11:03:15 -0700204
205
206def delete_and_upload(storage, out_dir, leak_temp_dir):
207 """Deletes the temporary run directory and uploads results back.
208
209 Returns:
210 tuple(outputs_ref, success)
211 - outputs_ref is a dict referring to the results archived back to the
212 isolated server, if applicable.
213 - success is False if something occurred that means that the task must
214 forcibly be considered a failure, e.g. zombie processes were left behind.
215 """
216
217 # Upload out_dir and generate a .isolated file out of this directory. It is
218 # only done if files were written in the directory.
219 outputs_ref = None
220 if os.path.isdir(out_dir) and os.listdir(out_dir):
221 with tools.Profiler('ArchiveOutput'):
222 try:
223 results = isolateserver.archive_files_to_storage(
224 storage, [out_dir], None)
225 outputs_ref = {
226 'isolated': results[0][0],
227 'isolatedserver': storage.location,
228 'namespace': storage.namespace,
229 }
230 except isolateserver.Aborted:
231 # This happens when a signal SIGTERM was received while uploading data.
232 # There is 2 causes:
233 # - The task was too slow and was about to be killed anyway due to
234 # exceeding the hard timeout.
235 # - The amount of data uploaded back is very large and took too much
236 # time to archive.
237 sys.stderr.write('Received SIGTERM while uploading')
238 # Re-raise, so it will be treated as an internal failure.
239 raise
240 try:
maruel6eeea7d2015-09-16 12:17:42 -0700241 if (not leak_temp_dir and os.path.isdir(out_dir) and
242 not file_path.rmtree(out_dir)):
maruela9cfd6f2015-09-15 11:03:15 -0700243 logging.error('Had difficulties removing out_dir %s', out_dir)
244 return outputs_ref, False
245 except OSError as e:
246 # When this happens, it means there's a process error.
247 logging.error('Had difficulties removing out_dir %s: %s', out_dir, e)
248 return outputs_ref, False
249 return outputs_ref, True
250
251
marueleb5fbee2015-09-17 13:01:36 -0700252def map_and_run(
maruel6be7f9e2015-10-01 12:25:30 -0700253 isolated_hash, storage, cache, leak_temp_dir, root_dir, hard_timeout,
254 grace_period, extra_args):
maruela9cfd6f2015-09-15 11:03:15 -0700255 """Maps and run the command. Returns metadata about the result."""
256 # TODO(maruel): Include performance statistics.
257 result = {
258 'exit_code': None,
maruel6be7f9e2015-10-01 12:25:30 -0700259 'had_hard_timeout': False,
maruela9cfd6f2015-09-15 11:03:15 -0700260 'internal_failure': None,
261 'outputs_ref': None,
maruel6be7f9e2015-10-01 12:25:30 -0700262 'version': 2,
maruela9cfd6f2015-09-15 11:03:15 -0700263 }
marueleb5fbee2015-09-17 13:01:36 -0700264 if root_dir:
265 if not os.path.isdir(root_dir):
266 os.makedirs(root_dir, 0700)
267 prefix = u''
268 else:
269 root_dir = os.path.dirname(cache.cache_dir) if cache.cache_dir else None
270 prefix = u'isolated_'
271 run_dir = make_temp_dir(prefix + u'run', root_dir)
272 out_dir = make_temp_dir(prefix + u'out', root_dir)
273 tmp_dir = make_temp_dir(prefix + u'tmp', root_dir)
maruela9cfd6f2015-09-15 11:03:15 -0700274 try:
275 bundle = isolateserver.fetch_isolated(
276 isolated_hash=isolated_hash,
277 storage=storage,
278 cache=cache,
279 outdir=run_dir,
280 require_command=True)
281
282 change_tree_read_only(run_dir, bundle.read_only)
283 cwd = os.path.normpath(os.path.join(run_dir, bundle.relative_cwd))
284 command = bundle.command + extra_args
285 file_path.ensure_command_has_abs_path(command, cwd)
maruel6be7f9e2015-10-01 12:25:30 -0700286 result['exit_code'], result['had_hard_timeout'] = run_command(
287 process_command(command, out_dir), cwd, tmp_dir, hard_timeout,
288 grace_period)
maruela9cfd6f2015-09-15 11:03:15 -0700289 except Exception as e:
290 # An internal error occured. Report accordingly so the swarming task will be
291 # retried automatically.
292 logging.error('internal failure: %s', e)
293 result['internal_failure'] = str(e)
294 on_error.report(None)
295 finally:
296 try:
297 if leak_temp_dir:
298 logging.warning(
299 'Deliberately leaking %s for later examination', run_dir)
marueleb5fbee2015-09-17 13:01:36 -0700300 else:
301 if os.path.isdir(run_dir) and not file_path.rmtree(run_dir):
302 # On Windows rmtree(run_dir) call above has a synchronization effect:
303 # it finishes only when all task child processes terminate (since a
304 # running process locks *.exe file). Examine out_dir only after that
305 # call completes (since child processes may write to out_dir too and
306 # we need to wait for them to finish).
307 print >> sys.stderr, (
308 'Failed to delete the run directory, forcibly failing\n'
309 'the task because of it. No zombie process can outlive a\n'
310 'successful task run and still be marked as successful.\n'
311 'Fix your stuff.')
312 if result['exit_code'] == 0:
313 result['exit_code'] = 1
314 if os.path.isdir(tmp_dir) and not file_path.rmtree(tmp_dir):
315 print >> sys.stderr, (
316 'Failed to delete the temporary directory, forcibly failing\n'
317 'the task because of it. No zombie process can outlive a\n'
318 'successful task run and still be marked as successful.\n'
319 'Fix your stuff.')
320 if result['exit_code'] == 0:
321 result['exit_code'] = 1
maruela9cfd6f2015-09-15 11:03:15 -0700322
marueleb5fbee2015-09-17 13:01:36 -0700323 # This deletes out_dir if leak_temp_dir is not set.
maruela9cfd6f2015-09-15 11:03:15 -0700324 result['outputs_ref'], success = delete_and_upload(
325 storage, out_dir, leak_temp_dir)
326 if not success and result['exit_code'] == 0:
327 result['exit_code'] = 1
328 except Exception as e:
329 # Swallow any exception in the main finally clause.
330 logging.error('Leaking out_dir %s: %s', out_dir, e)
331 result['internal_failure'] = str(e)
332 return result
Marc-Antoine Ruel2283ad12014-02-09 11:14:57 -0500333
334
Marc-Antoine Ruel0ec868b2015-08-12 14:12:46 -0400335def run_tha_test(
marueleb5fbee2015-09-17 13:01:36 -0700336 isolated_hash, storage, cache, leak_temp_dir, result_json, root_dir,
maruel6be7f9e2015-10-01 12:25:30 -0700337 hard_timeout, grace_period, extra_args):
Marc-Antoine Ruel2283ad12014-02-09 11:14:57 -0500338 """Downloads the dependencies in the cache, hardlinks them into a temporary
339 directory and runs the executable from there.
340
341 A temporary directory is created to hold the output files. The content inside
342 this directory will be uploaded back to |storage| packaged as a .isolated
343 file.
344
345 Arguments:
Marc-Antoine Ruel35b58432014-12-08 17:40:40 -0500346 isolated_hash: the SHA-1 of the .isolated file that must be retrieved to
Marc-Antoine Ruel2283ad12014-02-09 11:14:57 -0500347 recreate the tree of files to run the target executable.
348 storage: an isolateserver.Storage object to retrieve remote objects. This
349 object has a reference to an isolateserver.StorageApi, which does
350 the actual I/O.
351 cache: an isolateserver.LocalCache to keep from retrieving the same objects
352 constantly by caching the objects retrieved. Can be on-disk or
353 in-memory.
Kenneth Russell61d42352014-09-15 11:41:16 -0700354 leak_temp_dir: if true, the temporary directory will be deliberately leaked
355 for later examination.
maruela9cfd6f2015-09-15 11:03:15 -0700356 result_json: file path to dump result metadata into. If set, the process
357 exit code is always 0 unless an internal error occured.
marueleb5fbee2015-09-17 13:01:36 -0700358 root_dir: directory to the path to use to create the temporary directory. If
359 not specified, a random temporary directory is created.
maruel6be7f9e2015-10-01 12:25:30 -0700360 hard_timeout: kills the process if it lasts more than this amount of
361 seconds.
362 grace_period: number of seconds to wait between SIGTERM and SIGKILL.
Marc-Antoine Ruel2283ad12014-02-09 11:14:57 -0500363 extra_args: optional arguments to add to the command stated in the .isolate
364 file.
maruela9cfd6f2015-09-15 11:03:15 -0700365
366 Returns:
367 Process exit code that should be used.
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +0000368 """
maruela9cfd6f2015-09-15 11:03:15 -0700369 # run_isolated exit code. Depends on if result_json is used or not.
370 result = map_and_run(
maruel6be7f9e2015-10-01 12:25:30 -0700371 isolated_hash, storage, cache, leak_temp_dir, root_dir, hard_timeout,
372 grace_period, extra_args)
maruela9cfd6f2015-09-15 11:03:15 -0700373 logging.info('Result:\n%s', tools.format_json(result, dense=True))
374 if result_json:
maruel05d5a882015-09-21 13:59:02 -0700375 # We've found tests to delete 'work' when quitting, causing an exception
376 # here. Try to recreate the directory if necessary.
377 work_dir = os.path.dirname(result_json)
378 if not os.path.isdir(work_dir):
379 os.mkdir(work_dir)
maruela9cfd6f2015-09-15 11:03:15 -0700380 tools.write_json(result_json, result, dense=True)
381 # Only return 1 if there was an internal error.
382 return int(bool(result['internal_failure']))
maruel@chromium.org781ccf62013-09-17 19:39:47 +0000383
maruela9cfd6f2015-09-15 11:03:15 -0700384 # Marshall into old-style inline output.
385 if result['outputs_ref']:
386 data = {
387 'hash': result['outputs_ref']['isolated'],
388 'namespace': result['outputs_ref']['namespace'],
389 'storage': result['outputs_ref']['isolatedserver'],
390 }
Marc-Antoine Ruelc44f5722015-01-08 16:10:01 -0500391 sys.stdout.flush()
maruela9cfd6f2015-09-15 11:03:15 -0700392 print(
393 '[run_isolated_out_hack]%s[/run_isolated_out_hack]' %
394 tools.format_json(data, dense=True))
395 return result['exit_code'] or int(bool(result['internal_failure']))
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +0000396
397
Marc-Antoine Ruel90c98162013-12-18 15:11:57 -0500398def main(args):
vadimsh@chromium.orga4326472013-08-24 02:05:41 +0000399 tools.disable_buffering()
Marc-Antoine Ruelf74cffe2015-07-15 15:21:34 -0400400 parser = logging_utils.OptionParserWithLogging(
maruel@chromium.orgdedbf492013-09-12 20:42:11 +0000401 usage='%prog <options>',
402 version=__version__,
403 log_file=RUN_ISOLATED_LOG_FILE)
maruela9cfd6f2015-09-15 11:03:15 -0700404 parser.add_option(
405 '--json',
406 help='dump output metadata to json file. When used, run_isolated returns '
407 'non-zero only on internal failure')
maruel6be7f9e2015-10-01 12:25:30 -0700408 parser.add_option(
409 '--hard-timeout', type='int', help='Enforce hard timeout in execution')
410 parser.add_option(
411 '--grace-period', type='int',
412 help='Grace period between SIGTERM and SIGKILL')
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500413 data_group = optparse.OptionGroup(parser, 'Data source')
414 data_group.add_option(
Marc-Antoine Ruel185ded42015-01-28 20:49:18 -0500415 '-s', '--isolated',
416 help='Hash of the .isolated to grab from the isolate server')
Marc-Antoine Ruelf7d737d2014-12-10 15:36:29 -0500417 isolateserver.add_isolate_server_options(data_group)
Marc-Antoine Ruel1687b5e2014-02-06 17:47:53 -0500418 parser.add_option_group(data_group)
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +0000419
Marc-Antoine Ruela57d7db2014-10-15 20:31:19 -0400420 isolateserver.add_cache_options(parser)
421 parser.set_defaults(cache='cache')
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +0000422
Kenneth Russell61d42352014-09-15 11:41:16 -0700423 debug_group = optparse.OptionGroup(parser, 'Debugging')
424 debug_group.add_option(
425 '--leak-temp-dir',
426 action='store_true',
427 help='Deliberately leak isolate\'s temp dir for later examination '
428 '[default: %default]')
marueleb5fbee2015-09-17 13:01:36 -0700429 debug_group.add_option(
430 '--root-dir', help='Use a directory instead of a random one')
Kenneth Russell61d42352014-09-15 11:41:16 -0700431 parser.add_option_group(debug_group)
432
Vadim Shtayurae34e13a2014-02-02 11:23:26 -0800433 auth.add_auth_options(parser)
Marc-Antoine Ruel90c98162013-12-18 15:11:57 -0500434 options, args = parser.parse_args(args)
Marc-Antoine Ruel185ded42015-01-28 20:49:18 -0500435 if not options.isolated:
436 parser.error('--isolated is required.')
Vadim Shtayura5d1efce2014-02-04 10:55:43 -0800437 auth.process_auth_options(parser, options)
Marc-Antoine Ruele290ada2014-12-10 19:48:49 -0500438 isolateserver.process_isolate_server_options(parser, options, True)
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +0000439
Marc-Antoine Ruela57d7db2014-10-15 20:31:19 -0400440 cache = isolateserver.process_cache_options(options)
Marc-Antoine Ruelf7d737d2014-12-10 15:36:29 -0500441 with isolateserver.get_storage(
442 options.isolate_server, options.namespace) as storage:
Marc-Antoine Ruelcfb60852014-07-02 15:22:00 -0400443 # Hashing schemes used by |storage| and |cache| MUST match.
444 assert storage.hash_algo == cache.hash_algo
445 return run_tha_test(
Marc-Antoine Ruel0ec868b2015-08-12 14:12:46 -0400446 options.isolated, storage, cache, options.leak_temp_dir, options.json,
maruel6be7f9e2015-10-01 12:25:30 -0700447 options.root_dir, options.hard_timeout, options.grace_period, args)
maruel@chromium.org9c72d4e2012-09-28 19:20:25 +0000448
449
450if __name__ == '__main__':
csharp@chromium.orgbfb98742013-03-26 20:28:36 +0000451 # Ensure that we are always running with the correct encoding.
vadimsh@chromium.orga4326472013-08-24 02:05:41 +0000452 fix_encoding.fix_encoding()
Marc-Antoine Ruel90c98162013-12-18 15:11:57 -0500453 sys.exit(main(sys.argv[1:]))