Enable wrapping 'tee' functionality around build_api commands.

Add args to build_api to use cbuildbot/tee.py rather than tee_wrapper.
The wrapper functionality could be used in recipe code to wrap calls so that
stdout/stderr goes to the screen and is saved in a file that can be read and
stored by analysis engine to be queried later by dremel.

BUG=chromium:1012460
TEST=manual

Change-Id: Ia980ee056a6b31cd06c204eb85552d4ec8e71f8c
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/1929762
Tested-by: Michael Mortensen <mmortensen@google.com>
Commit-Queue: Michael Mortensen <mmortensen@google.com>
Reviewed-by: Alex Klein <saklein@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Will Bradley <wbbradley@chromium.org>
diff --git a/scripts/build_api.py b/scripts/build_api.py
index 2ca746d..339ac2e 100644
--- a/scripts/build_api.py
+++ b/scripts/build_api.py
@@ -15,6 +15,8 @@
 from chromite.api import router as router_lib
 from chromite.lib import commandline
 from chromite.lib import cros_build_lib
+from chromite.lib import cros_logging as logging
+from chromite.lib import tee
 from chromite.utils import matching
 
 
@@ -38,6 +40,10 @@
       '--output-json',
       type='path',
       help='The path to which the result protobuf message should be written.')
+  call_group.add_argument(
+      '--tee-log',
+      type='path',
+      help='The path to which stdout and stderr should be teed to.')
 
   ux_group = parser.add_argument_group('Developer Options',
                                        'Options to help developers.')
@@ -152,20 +158,25 @@
 
 
 def main(argv):
-  router = router_lib.GetRouter()
+  with cros_build_lib.ContextManagerStack() as stack:
 
-  opts = _ParseArgs(argv, router)
+    router = router_lib.GetRouter()
+    opts = _ParseArgs(argv, router)
 
-  if opts.mock_invalid:
-    # --mock-invalid handling. We print error messages, but no output is ever
-    # set for validation errors, so we can handle it by just giving back the
-    # correct return code here.
-    return controller.RETURN_CODE_INVALID_INPUT
+    if opts.tee_log:
+      stack.Add(tee.Tee, opts.tee_log)
+      logging.info('Teeing stdout and stderr to %s', opts.tee_log)
 
-  try:
-    return router.Route(opts.service, opts.method, opts.input_json,
-                        opts.output_json, opts.config)
-  except router_lib.Error as e:
-    # Handle router_lib.Error derivatives nicely, but let anything else bubble
-    # up.
-    cros_build_lib.Die(e)
+    if opts.mock_invalid:
+      # --mock-invalid handling. We print error messages, but no output is ever
+      # set for validation errors, so we can handle it by just giving back the
+      # correct return code here.
+      return controller.RETURN_CODE_INVALID_INPUT
+
+    try:
+      return router.Route(opts.service, opts.method, opts.input_json,
+                          opts.output_json, opts.config)
+    except router_lib.Error as e:
+      # Handle router_lib.Error derivatives nicely, but let anything else bubble
+      # up.
+      cros_build_lib.Die(e)