Report all swarming and isolate fatal errors in a consistent way.
It makes them easier to extract with regular expressions in log parsing tools.
BUG=309661
Review URL: https://codereview.chromium.org/51383003
git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/swarm_client@231704 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/isolate.py b/isolate.py
index 9ab79a8..73db3c1 100755
--- a/isolate.py
+++ b/isolate.py
@@ -2449,13 +2449,8 @@
dispatcher = subcommand.CommandDispatcher(__name__)
try:
return dispatcher.execute(OptionParserIsolate(version=__version__), argv)
- except (
- ExecutionError,
- isolateserver.ConfigError,
- isolateserver.MappingError) as e:
- sys.stderr.write('\nError: ')
- sys.stderr.write(str(e))
- sys.stderr.write('\n')
+ except Exception as e:
+ tools.report_error(e)
return 1
diff --git a/isolateserver.py b/isolateserver.py
index d2ad16b..93575a4 100755
--- a/isolateserver.py
+++ b/isolateserver.py
@@ -1652,10 +1652,8 @@
try:
return dispatcher.execute(
OptionParserIsolateServer(version=__version__), args)
- except (ConfigError, MappingError) as e:
- sys.stderr.write('\nError: ')
- sys.stderr.write(str(e))
- sys.stderr.write('\n')
+ except Exception as e:
+ tools.report_error(e)
return 1
diff --git a/run_isolated.py b/run_isolated.py
index dab90b3..54f36d4 100755
--- a/run_isolated.py
+++ b/run_isolated.py
@@ -497,7 +497,7 @@
os_flavor=get_flavor(),
require_command=True)
except isolateserver.ConfigError as e:
- print >> sys.stderr, str(e)
+ tools.report_error(e)
return 1
if settings.read_only:
@@ -517,7 +517,7 @@
with tools.Profiler('RunTest'):
return subprocess.call(settings.command, cwd=cwd, env=env)
except OSError:
- print >> sys.stderr, 'Failed to run %s; cwd=%s' % (settings.command, cwd)
+ tools.report_error('Failed to run %s; cwd=%s' % (settings.command, cwd))
return 1
finally:
if outdir:
@@ -602,6 +602,7 @@
options.isolated or options.hash, storage, cache, algo, outdir)
except Exception as e:
# Make sure any exception is logged.
+ tools.report_error(e)
logging.exception(e)
return 1
diff --git a/swarming.py b/swarming.py
index cc30dc5..4c8f8f0 100755
--- a/swarming.py
+++ b/swarming.py
@@ -133,7 +133,7 @@
uploaded = self.storage.upload_items([self._isolate_item])
elapsed = time.time() - start_time
except (IOError, OSError) as exc:
- print >> sys.stderr, 'Failed to upload the zip file: %s' % exc
+ tools.report_error('Failed to upload the zip file: %s' % exc)
return False
if self._isolate_item in uploaded:
@@ -349,12 +349,12 @@
file_hash = archive(
file_hash_or_isolated, isolate_server, slave_os, algo, verbose)
if not file_hash:
- print >> sys.stderr, 'Archival failure %s' % file_hash_or_isolated
+ tools.report_error('Archival failure %s' % file_hash_or_isolated)
return 1
elif isolateserver.is_valid_hash(file_hash_or_isolated, algo):
file_hash = file_hash_or_isolated
else:
- print >> sys.stderr, 'Invalid hash %s' % file_hash_or_isolated
+ tools.report_error('Invalid hash %s' % file_hash_or_isolated)
return 1
try:
@@ -371,7 +371,7 @@
priority,
algo)
except ValueError as e:
- print >> sys.stderr, 'Unable to process %s: %s' % (test_name, e)
+ tools.report_error('Unable to process %s: %s' % (test_name, e))
return 1
chromium_setup(manifest)
@@ -389,16 +389,18 @@
manifest_text = manifest.to_json()
result = net.url_read(test_url, data={'request': manifest_text})
if not result:
- print >> sys.stderr, 'Failed to send test for %s\n%s' % (
- test_name, test_url)
+ tools.report_error(
+ 'Failed to send test for %s\n%s' % (test_name, test_url))
return 1
try:
json.loads(result)
except (ValueError, TypeError) as e:
- print >> sys.stderr, 'Failed to send test for %s' % test_name
- print >> sys.stderr, 'Manifest: %s' % manifest_text
- print >> sys.stderr, 'Bad response: %s' % result
- print >> sys.stderr, str(e)
+ msg = '\n'.join((
+ 'Failed to send test for %s' % test_name,
+ 'Manifest: %s' % manifest_text,
+ 'Bad response: %s' % result,
+ str(e)))
+ tools.report_error(msg)
return 1
return 0
@@ -540,7 +542,8 @@
try:
return collect(options.swarming, args[0], options.timeout, options.decorate)
except Failure as e:
- parser.error(e.args[0])
+ tools.report_error(e)
+ return 1
@subcommand.usage('[hash|isolated ...]')
@@ -574,12 +577,12 @@
except Failure as e:
result = e.args[0]
if result:
- print >> sys.stderr, 'Failed to trigger %s: %s' % (arg, result)
+ tools.report_error('Failed to trigger %s: %s' % (arg, result))
else:
success.append(os.path.basename(arg))
if not success:
- print >> sys.stderr, 'Failed to trigger any job.'
+ tools.report_error('Failed to trigger any job.')
return result
code = 0
@@ -594,7 +597,7 @@
code = max(code, new_code)
except Failure as e:
code = max(code, 1)
- print >> sys.stderr, e.args[0]
+ tools.report_error(e)
return code
@@ -632,7 +635,8 @@
options.profile,
options.priority)
except Failure as e:
- parser.error(e.args[0])
+ tools.report_error(e)
+ return 1
class OptionParserSwarming(tools.OptionParserWithLogging):
@@ -657,10 +661,8 @@
dispatcher = subcommand.CommandDispatcher(__name__)
try:
return dispatcher.execute(OptionParserSwarming(version=__version__), args)
- except Failure as e:
- sys.stderr.write('\nError: ')
- sys.stderr.write(str(e))
- sys.stderr.write('\n')
+ except Exception as e:
+ tools.report_error(e)
return 1
diff --git a/tests/isolate_smoke_test.py b/tests/isolate_smoke_test.py
index d2e66e8..9adab06 100755
--- a/tests/isolate_smoke_test.py
+++ b/tests/isolate_smoke_test.py
@@ -364,11 +364,10 @@
self._expect_no_result()
root = file_path.get_native_path_case(unicode(ROOT_DIR))
expected = (
- '\n'
- 'Error: Input directory %s must have a trailing slash\n' %
+ 'Input directory %s must have a trailing slash' %
os.path.join(root, 'tests', 'isolate', 'files1')
)
- self.assertEqual(expected, out)
+ self.assertIn(expected, out)
def _test_non_existent(self, mode):
try:
@@ -381,11 +380,10 @@
self._expect_no_result()
root = file_path.get_native_path_case(unicode(ROOT_DIR))
expected = (
- '\n'
- 'Error: Input file %s doesn\'t exist\n' %
+ 'Input file %s doesn\'t exist' %
os.path.join(root, 'tests', 'isolate', 'A_file_that_do_not_exist')
)
- self.assertEqual(expected, out)
+ self.assertIn(expected, out)
def _test_all_items_invalid(self, mode):
out = self._execute(mode, 'all_items_invalid.isolate',
@@ -842,9 +840,9 @@
err = e.stderr
self._expect_no_tree()
self._expect_no_result()
- expected = '\nError: No command to run.\n'
+ expected = 'No command to run.'
self.assertEqual('', out)
- self.assertEqual(expected, err)
+ self.assertIn(expected, err)
# TODO(csharp): Disabled until crbug.com/150823 is fixed.
def do_not_test_touch_only(self):
diff --git a/tests/run_isolated_smoke_test.py b/tests/run_isolated_smoke_test.py
index 45839b4..49f6984 100755
--- a/tests/run_isolated_smoke_test.py
+++ b/tests/run_isolated_smoke_test.py
@@ -187,7 +187,7 @@
out, err, returncode = self._run(self._generate_args_with_hash(result_hash))
if not VERBOSE:
self.assertEqual('', out)
- self.assertEqual('No command to run\n', err)
+ self.assertIn('No command to run\n', err)
self.assertEqual(1, returncode)
actual = list_files_tree(self.cache)
self.assertEqual(sorted(expected), actual)
diff --git a/utils/tools.py b/utils/tools.py
index eecf709..2885954 100644
--- a/utils/tools.py
+++ b/utils/tools.py
@@ -10,6 +10,7 @@
import os
import sys
import time
+import traceback
class OptionParserWithLogging(optparse.OptionParser):
@@ -112,3 +113,23 @@
elif out[0].endswith('.py'):
out.insert(0, sys.executable)
return out
+
+
+def report_error(error):
+ """Prints a error to stderr, wrapping it into header and footer.
+
+ That way errors can be reliably extracted from logs. It's indented to be used
+ only for non recoverable unexpected errors. Is should NOT be used for input
+ validation, command line argument errors, etc.
+
+ Arguments:
+ error: error message string (possibly multiple lines) or an instance of
+ Exception subclass. In the later case a traceback will also be
+ reported. It's assumed that |report_error| is called in an except
+ block where |error| was caught.
+ """
+ print >> sys.stderr, '[------ Swarming Error ------]'
+ print >> sys.stderr, str(error)
+ if isinstance(error, Exception):
+ print >> sys.stderr, traceback.format_exc(),
+ print >> sys.stderr, '[----------------------------]'