build_api: add handlers for tot fw builder API

Add implementation for new tot firmware builder API.

See also go/cros-tot-fw-builder

BUG=b:169178847
TEST=chromite/api/contrib/call_scripts/firmware__build_all_tot_firmware
calls into firmware_builder.py correctly in platform/ec and the
results are serialized correctly as json. (Same with test endpoint
call_script too)

Change-Id: Ifb391e15212fd24aeff3c63a22c6aff1d9c7da9f
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/2432871
Commit-Queue: Jett Rink <jettrink@chromium.org>
Tested-by: Jett Rink <jettrink@chromium.org>
Reviewed-by: Alex Klein <saklein@chromium.org>
diff --git a/api/controller/firmware.py b/api/controller/firmware.py
new file mode 100644
index 0000000..4b0ffbc
--- /dev/null
+++ b/api/controller/firmware.py
@@ -0,0 +1,86 @@
+# -*- coding: utf-8 -*-
+# Copyright 2020 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Firmware builder controller.
+
+Handle all firmware builder related functionality.
+"""
+
+import os
+import tempfile
+
+from google.protobuf import json_format
+
+from chromite.api import controller
+from chromite.api import faux
+from chromite.api import validate
+from chromite.api.gen.chromite.api import firmware_pb2
+from chromite.lib import constants
+from chromite.lib import cros_build_lib
+
+def _call_entry(fw_loc, metric_proto, subcmd):
+  """Calls into firmware_builder.py with the specified subcmd."""
+
+  if fw_loc == firmware_pb2.PLATFORM_EC:
+    fw_path = 'src/platform/ec/'
+  elif fw_loc == firmware_pb2.PLATFORM_ZEPHYR:
+    fw_path = 'src/platform/zephyr-chrome/'
+  else:
+    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:
+      response = f.read()
+
+  # Parse the entire metric file as our metric proto (as a passthru)
+  json_format.Parse(response, metric_proto)
+
+  if result.returncode == 0:
+    return controller.RETURN_CODE_SUCCESS
+  else:
+    return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
+
+
+def _BuildAllTotFirmwareResponse(_input_proto, output_proto, _config):
+  """Add a fw region metric to a successful repose."""
+
+  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(_BuildAllTotFirmwareResponse)
+@faux.empty_completed_unsuccessfully_error
+@validate.require('firmware_location')
+@validate.validation_complete
+def BuildAllTotFirmware(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 _TestAllTotFirmwareResponse(_input_proto, output_proto, _config):
+  """Add a fw region metric to a successful repose."""
+
+  metric = output_proto.success.value.add()
+  metric.name = 'foo-test'
+
+@faux.success(_TestAllTotFirmwareResponse)
+@faux.empty_completed_unsuccessfully_error
+@validate.require('firmware_location')
+@validate.validation_complete
+def TestAllTotFirmware(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')