firmware.py: Add FirmwareService methods.

This adds a call to:
  ${PATH}/firmware_builder.py bundle --output-dir ${DIR} --metadata ${FILE}

BUG=b:177907747
TEST=led

Change-Id: Ie44c27c9242fdec002d055990b0db0164e7c3717
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/2654588
Reviewed-by: Michael Mortensen <mmortensen@google.com>
Commit-Queue: LaMont Jones <lamontjones@chromium.org>
Tested-by: LaMont Jones <lamontjones@chromium.org>
Auto-Submit: LaMont Jones <lamontjones@chromium.org>
diff --git a/api/controller/firmware.py b/api/controller/firmware.py
index fcb2b78..cc8e5e0 100644
--- a/api/controller/firmware.py
+++ b/api/controller/firmware.py
@@ -4,7 +4,8 @@
 # found in the LICENSE file.
 """Firmware builder controller.
 
-Handle all firmware builder related functionality.
+Handle all firmware builder related functionality.  Currently no service module
+exists: all of the work is done here.
 """
 
 import os
@@ -16,28 +17,32 @@
 from chromite.api import faux
 from chromite.api import validate
 from chromite.api.gen.chromite.api import firmware_pb2
+from chromite.api.gen.chromiumos import common_pb2
 from chromite.lib import constants
 from chromite.lib import cros_build_lib
 
-def _call_entry(fw_loc, metric_proto, subcmd):
+def _call_entry(fw_loc, metric_proto, subcmd, **kwargs):
   """Calls into firmware_builder.py with the specified subcmd."""
 
-  if fw_loc == firmware_pb2.PLATFORM_EC:
+  if fw_loc == common_pb2.PLATFORM_EC:
     fw_path = 'src/platform/ec/'
-  elif fw_loc == firmware_pb2.PLATFORM_ZEPHYR:
+  elif fw_loc == common_pb2.PLATFORM_ZEPHYR:
     fw_path = 'src/platform/zephyr-chrome/'
-  elif fw_loc == firmware_pb2.PLATFORM_TI50:
+  elif fw_loc == common_pb2.PLATFORM_TI50:
     fw_path = 'src/platform/ti50/common/'
   else:
-    cros_build_lib.Die(f'Unknown firmware location {fw_loc}!')
+    cros_build_lib.Die(f'Unknown firmware location {fw_loc}.')
 
   entry_point = os.path.join(constants.SOURCE_ROOT,
                              fw_path, 'firmware_builder.py')
 
-  with tempfile.NamedTemporaryFile() as file:
-    result = cros_build_lib.run([entry_point, '--metrics', file.name, subcmd],
-                                check=False)
-    with open(file.name, 'r') as f:
+  with tempfile.NamedTemporaryFile() as tmpfile:
+    cmd = [entry_point, '--metrics', tmpfile.name, subcmd]
+    for key, value in kwargs.items():
+      cmd += [f'--{key.replace("_", "-")}', value]
+
+    result = cros_build_lib.run(cmd, check=False)
+    with open(tmpfile.name, 'r') as f:
       response = f.read()
 
   # Parse the entire metric file as our metric proto (as a passthru)
@@ -50,7 +55,7 @@
 
 
 def _BuildAllTotFirmwareResponse(_input_proto, output_proto, _config):
-  """Add a fw region metric to a successful repose."""
+  """Add a fw region metric to a successful response."""
 
   metric = output_proto.success.value.add()
   metric.target_name = 'foo'
@@ -72,7 +77,7 @@
 
 
 def _TestAllTotFirmwareResponse(_input_proto, output_proto, _config):
-  """Add a fw region metric to a successful repose."""
+  """Add a fw region metric to a successful response."""
 
   metric = output_proto.success.value.add()
   metric.name = 'foo-test'
@@ -86,3 +91,78 @@
 
   return _call_entry(input_proto.firmware_location, output_proto.metrics,
                      'test')
+
+
+def _BuildAllFirmwareResponse(_input_proto, output_proto, _config):
+  """Add a fw region metric to a successful response."""
+
+  metric = output_proto.success.value.add()
+  metric.target_name = 'foo'
+  metric.platform_name = 'bar'
+  fw_section = metric.fw_section.add()
+  fw_section.region = firmware_pb2.FwBuildMetric.FwSection.EC_RO
+  fw_section.used = 100
+  fw_section.total = 150
+
+@faux.success(_BuildAllFirmwareResponse)
+@faux.empty_completed_unsuccessfully_error
+@validate.require('firmware_location')
+@validate.validation_complete
+def BuildAllFirmware(input_proto, output_proto, _config):
+  """Build all of the firmware targets at the specified location."""
+
+  return _call_entry(input_proto.firmware_location, output_proto.metrics,
+                     'build')
+
+
+def _TestAllFirmwareResponse(_input_proto, output_proto, _config):
+  """Add a fw region metric to a successful response."""
+
+  metric = output_proto.success.value.add()
+  metric.name = 'foo-test'
+
+@faux.success(_TestAllFirmwareResponse)
+@faux.empty_completed_unsuccessfully_error
+@validate.require('firmware_location')
+@validate.validation_complete
+def TestAllFirmware(input_proto, output_proto, _config):
+  """Runs all of the firmware tests at the specified location."""
+
+  return _call_entry(input_proto.firmware_location, output_proto.metrics,
+                     'test')
+
+
+def _BundleFirmwareArtifactsResponse(_input_proto, output_proto, _config):
+  """Add a fw region metric to a successful response."""
+
+  metric = output_proto.success.value.add()
+  metric.name = 'foo-test'
+
+@faux.success(_BundleFirmwareArtifactsResponse)
+@faux.empty_completed_unsuccessfully_error
+@validate.require('firmware_location')
+@validate.validation_complete
+def BundleFirmwareArtifacts(input_proto, output_proto, _config):
+  """Runs all of the firmware tests at the specified location."""
+
+  if len(input_proto.output_artifacts) > 1:
+    raise ValueError('Must have exactly one output_artifact')
+
+  with tempfile.NamedTemporaryFile() as meta:
+    info = input_proto.output_artifacts[0]
+    metadata_path = os.path.join(input_proto.result_path.path, meta.name)
+    resp = _call_entry(
+        info.location, output_proto.metrics, 'bundle',
+        output_dir=input_proto.result_path.path, metadata=metadata_path)
+    if common_pb2.FIRMWARE_TARBALL in info.artifact_types:
+      out = output_proto.artifacts.add()
+      out.artifact_types.append(common_pb2.FIRMWARE_TARBALL)
+      # TODO(b/177907747): gather the paths from the response and add them to
+      # out.paths.
+      out.location = info.location
+    if common_pb2.FIRMWARE_TARBALL_INFO in info.artifact_types:
+      out = output_proto.artifacts.add()
+      out.artifact_types.append(common_pb2.FIRMWARE_TARBALL_INFO)
+      out.paths.append(
+          common_pb2.Path(metadata_path, input_proto.result_path.path.location))
+    return resp