Do not consider missing command as internal failure.

If there is no command specified in the .isolated, which is because there was
none in the .isolate. It is in fact a user error, not an internal error. Report
this accordingly, so that the recipe do not ignore this error.

R=tandrii@chromium.org
BUG=

Review URL: https://codereview.chromium.org/1734743002

Cr-Mirrored-From: https://github.com/luci/luci-py
Cr-Mirrored-Commit: 7272431afa6028a3c5a4525a35dc316b88513b87
diff --git a/run_isolated.py b/run_isolated.py
index 3d0b18b..fb4f128 100755
--- a/run_isolated.py
+++ b/run_isolated.py
@@ -195,6 +195,15 @@
     except OSError:
       # This is not considered to be an internal error. The executable simply
       # does not exit.
+      sys.stderr.write(
+          '<The executable does not exist or a dependent library is missing>\n'
+          '<Check for missing .so/.dll in the .isolate or GN file>\n'
+          '<Command: %s>\n' % command)
+      if os.environ.get('SWARMING_TASK_ID'):
+        # Give an additional hint when running as a swarming task.
+        sys.stderr.write(
+            '<See the task\'s page for commands to help diagnose this issue '
+            'by reproducing the task locally>\n')
       exit_code = 1
   logging.info(
       'Command finished with exit code %d (%s)',
@@ -271,12 +280,23 @@
   out_dir = make_temp_dir(prefix + u'out', root_dir)
   tmp_dir = make_temp_dir(prefix + u'tmp', root_dir)
   try:
-    bundle = isolateserver.fetch_isolated(
-        isolated_hash=isolated_hash,
-        storage=storage,
-        cache=cache,
-        outdir=run_dir,
-        require_command=True)
+    try:
+      bundle = isolateserver.fetch_isolated(
+          isolated_hash=isolated_hash,
+          storage=storage,
+          cache=cache,
+          outdir=run_dir,
+          require_command=True)
+    except isolateserver.IsolatedErrorNoCommand:
+      # Handle this as a task failure, not an internal failure.
+      sys.stderr.write(
+          '<The .isolated doesn\'t declare any command to run!>\n'
+          '<Check your .isolate for missing \'command\' variable>\n')
+      if os.environ.get('SWARMING_TASK_ID'):
+        # Give an additional hint when running as a swarming task.
+        sys.stderr.write('<This occurs at the \'isolate\' step>\n')
+      result['exit_code'] = 1
+      return result
 
     change_tree_read_only(run_dir, bundle.read_only)
     cwd = os.path.normpath(os.path.join(run_dir, bundle.relative_cwd))