api: rewrite breakpad and add debug symbols

Breakpad symbols were named wrong, referring to DEBUG symbols. We
translate the existing DEBUG to BREAKPAD_DEBUG and we correct them.

In addition we intro a new model for the global Get function to call
the individual handlers. Finally, DEBUG is also implemented.

BUG=b:185593007
TEST=call_scripts && units

Change-Id: I9f080c1261387e74ceb177a9994186883b57a347
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/2959731
Tested-by: George Engelbrecht <engeg@google.com>
Reviewed-by: Alex Klein <saklein@chromium.org>
Commit-Queue: George Engelbrecht <engeg@google.com>
diff --git a/api/controller/artifacts.py b/api/controller/artifacts.py
index a7e6bf5..1a62605 100644
--- a/api/controller/artifacts.py
+++ b/api/controller/artifacts.py
@@ -5,11 +5,14 @@
 """Implements ArtifactService."""
 
 import os
+from typing import Any, NamedTuple
 
 from chromite.api import controller
 from chromite.api import faux
 from chromite.api import validate
 from chromite.api.controller import controller_util
+from chromite.api.controller import image as image_controller
+from chromite.api.controller import sysroot as sysroot_controller
 from chromite.api.gen.chromite.api import artifacts_pb2
 from chromite.api.gen.chromite.api import toolchain_pb2
 from chromite.api.gen.chromiumos import common_pb2
@@ -19,17 +22,26 @@
 from chromite.lib import cros_logging as logging
 from chromite.lib import sysroot_lib
 from chromite.service import artifacts
-from chromite.service import image as image_service
 
 
-def _GetResponse(_input_proto, _output_proto, _config):
-  """Currently bundles nothing."""
-  # TODO(crbug/1034529): As methods migrate, begin populating them based on what
-  # input_proto has defined.
+class RegisteredGet(NamedTuple):
+  """An registered function for calling Get on an artifact type."""
+  output_proto: artifacts_pb2.GetResponse
+  artifact_dict: Any
 
 
-@faux.success(_GetResponse)
+def ExampleGetResponse(_input_proto, _output_proto, _config):
+  """Give an example GetResponse with a minimal coverage set."""
+  _output_proto = artifacts_pb2.GetResponse(
+      artifacts=common_pb2.UploadedArtifactsByService(
+          image=image_controller.ExampleGetResponse(),
+          sysroot=sysroot_controller.ExampleGetResponse(),
+      ))
+  return controller.RETURN_CODE_SUCCESS
+
+
 @faux.empty_error
+@faux.success(ExampleGetResponse)
 @validate.exists('result_path.path.path')
 @validate.validation_complete
 def Get(input_proto, output_proto, _config):
@@ -37,33 +49,44 @@
 
   Get all artifacts for the build.
 
-  Note: crbug/1034529 introduces this method as a noop.  As the individual
-  artifact_type bundlers are added here, they *must* stop uploading it via the
-  individual bundler function.
+  Note: As the individual artifact_type bundlers are added here, they *must*
+  stop uploading it via the individual bundler function.
 
   Args:
     input_proto (GetRequest): The input proto.
     output_proto (GetResponse): The output proto.
     _config (api_config.ApiConfig): The API call config.
   """
-
-  image_proto = input_proto.artifact_info.image
-  base_path = os.path.join(input_proto.chroot.path,
-                           input_proto.sysroot.path[1:])
   output_dir = input_proto.result_path.path.path
 
-  images_list = image_service.Get(image_proto, base_path, output_dir)
+  sysroot = controller_util.ParseSysroot(input_proto.sysroot)
+  chroot = controller_util.ParseChroot(input_proto.chroot)
+  build_target = controller_util.ParseBuildTarget(
+      input_proto.sysroot.build_target)
 
-  for artifact_dict in images_list:
-    output_proto.artifacts.image.artifacts.add(
-        artifact_type=artifact_dict['type'],
-        paths=[
-            common_pb2.Path(
-                path=x,
-                location=common_pb2.Path.Location.OUTSIDE)
-            for x in artifact_dict['paths']
-        ])
+  # A list of RegisteredGet tuples (input proto, output proto, get results).
+  get_res_list = [
+      RegisteredGet(
+          output_proto.artifacts.image,
+          image_controller.GetArtifacts(
+              input_proto.artifact_info.image, chroot, sysroot, build_target,
+              output_dir)),
+      RegisteredGet(
+          output_proto.artifacts.sysroot,
+          sysroot_controller.GetArtifacts(
+              input_proto.artifact_info.sysroot, chroot, sysroot, build_target,
+              output_dir))
+  ]
 
+  for get_res in get_res_list:
+    for artifact_dict in get_res.artifact_dict:
+      get_res.output_proto.artifacts.add(
+          artifact_type=artifact_dict['type'],
+          paths=[
+              common_pb2.Path(
+                  path=x, location=common_pb2.Path.Location.OUTSIDE)
+              for x in artifact_dict['paths']
+          ])
   return controller.RETURN_CODE_SUCCESS
 
 
@@ -674,59 +697,3 @@
 
   tarball = artifacts.BundleGceTarball(output_dir, image_dir)
   output_proto.artifacts.add().path = tarball
-
-
-def _BundleDebugSymbolsResponse(input_proto, output_proto, _config):
-  """Add artifact tarball to a successful response."""
-  output_proto.artifacts.add().path = os.path.join(input_proto.output_dir,
-                                                   constants.DEBUG_SYMBOLS_TAR)
-
-
-@faux.success(_BundleDebugSymbolsResponse)
-@faux.empty_error
-@validate.require('build_target.name', 'output_dir')
-@validate.exists('output_dir')
-@validate.validation_complete
-def BundleDebugSymbols(input_proto, output_proto, _config):
-  """Bundle the debug symbols into a tarball suitable for importing into GCE.
-
-  Args:
-    input_proto (BundleRequest): The input proto.
-    output_proto (BundleResponse): The output proto.
-    _config (api_config.ApiConfig): The API call config.
-  """
-  output_dir = input_proto.output_dir
-
-  chroot = controller_util.ParseChroot(input_proto.chroot)
-  build_target = controller_util.ParseBuildTarget(input_proto.build_target)
-  result = artifacts.GenerateBreakpadSymbols(chroot,
-                                             build_target,
-                                             debug=True)
-
-  # Verify breakpad symbol generation before gathering the sym files.
-  if result.returncode != 0:
-    return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
-
-  with chroot.tempdir() as symbol_tmpdir, chroot.tempdir() as dest_tmpdir:
-    breakpad_dir = os.path.join(chroot.path, 'build', build_target.name,
-                                'usr/lib/debug/breakpad')
-    # Call list on the atifacts.GatherSymbolFiles generator function to
-    # materialize and consume all entries so that all are copied to
-    # dest dir and complete list of all symbol files is returned.
-    sym_file_list = list(artifacts.GatherSymbolFiles(tempdir=symbol_tmpdir,
-                                                     destdir=dest_tmpdir,
-                                                     paths=[breakpad_dir]))
-    if not sym_file_list:
-      logging.warning('No sym files found in %s.', breakpad_dir)
-    # Create tarball from destination_tmp, then copy it...
-    tarball_path = os.path.join(output_dir, constants.DEBUG_SYMBOLS_TAR)
-    result = cros_build_lib.CreateTarball(tarball_path, dest_tmpdir)
-    if result.returncode != 0:
-      logging.error('Error (%d) when creating tarball %s from %s',
-                    result.returncode,
-                    tarball_path,
-                    dest_tmpdir)
-      return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
-    output_proto.artifacts.add().path = tarball_path
-
-  return controller.RETURN_CODE_SUCCESS