api: Add MakeNetboot endpoint.

The netboot kernel doesn't build everything it needs in build_packages
like the rest of the images. Split out the creation of the netboot
kernel bundle from ImageService/Create, which has network access
disabled, to allow it to complete, until the underlying problem has
been addressed.

BUG=b:239795601
TEST=manual

Cq-Depend: chromium:3971348
Change-Id: I1bcb4e30542cc6eae27623c2afb0cb787d8fab57
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/3971953
Commit-Queue: Alex Klein <saklein@chromium.org>
Tested-by: Alex Klein <saklein@chromium.org>
Reviewed-by: George Engelbrecht <engeg@google.com>
diff --git a/api/controller/image.py b/api/controller/image.py
index ef69e73..5961943 100644
--- a/api/controller/image.py
+++ b/api/controller/image.py
@@ -335,7 +335,10 @@
                 factory_shim_dir = os.path.dirname(
                     factory_result.images[constants.IMAGE_TYPE_FACTORY_SHIM]
                 )
-                image.create_netboot_kernel(board, factory_shim_dir)
+                try:
+                    image.create_netboot_kernel(board, factory_shim_dir)
+                except cros_build_lib.RunCommandError as e:
+                    logging.warning(e)
             else:
                 cros_build_lib.Die(
                     "_RECOVERY_ID and _NETBOOT_ID are the only mod_image_type."
@@ -456,6 +459,35 @@
     )
 
 
+@faux.all_empty
+@validate.require("build_target.name")
+@validate.validation_complete
+def CreateNetboot(input_proto, _output_proto, _config):
+    """Create a netboot kernel.
+
+    The netboot kernel currently needs network access because it's not building
+    everything in build_packages like other images. Once that has been remedied,
+    using Create to build the netboot kernel will be the expected workflow, and
+    this endpoint will be deprecated (b/255397725).
+    """
+    build_target = controller_util.ParseBuildTarget(input_proto.build_target)
+    if input_proto.factory_shim_path:
+        factory_shim_location = Path(input_proto.factory_shim_path).parent
+    else:
+        factory_shim_location = Path(
+            image_lib.GetLatestImageLink(
+                build_target.name, pointer=LOCATION_FACTORY
+            )
+        )
+    if not factory_shim_location.exists():
+        logging.warning(
+            "Factory shim directory does not exist. Skipping netboot creation."
+        )
+        return
+
+    image.create_netboot_kernel(build_target.name, str(factory_shim_location))
+
+
 def _SignerTestResponse(_input_proto, output_proto, _config):
     """Set output_proto success field on a successful SignerTest response."""
     output_proto.success = True