GeneratePayload: Gen miniOS payloads, if possible

Also some internal refactoring to support this change. Previously,
_Create returned a bool representing whether a payload was generated.
This omitted the critical information of _why_ a payload might not be
generated. I captured this information in the form of BaseException
subclasses, since exceptions are meant to represent breaks in the normal
codepath, and in this case the normal codepath for _Create is "creating"
a payload.

BUG=b:217556989
TEST=./run_tests.py

Change-Id: Ie94d5a046157df8dcd218ace019024bbdddf2604
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/chromite/+/3456639
Reviewed-by: Benjamin Shai <bshai@google.com>
Reviewed-by: Jack Neus <jackneus@google.com>
Reviewed-by: Sergey Frolov <sfrolov@google.com>
Auto-Submit: Greg Edelston <gredelston@google.com>
Tested-by: Greg Edelston <gredelston@google.com>
Commit-Queue: Greg Edelston <gredelston@google.com>
diff --git a/api/controller/payload.py b/api/controller/payload.py
index d0fa6a3..c1b9042 100644
--- a/api/controller/payload.py
+++ b/api/controller/payload.py
@@ -5,9 +5,11 @@
 """Payload API Service."""
 
 from chromite.api import controller
-from chromite.lib import cros_build_lib
 from chromite.api import faux
 from chromite.api import validate
+from chromite.api.gen.chromite.api import payload_pb2
+from chromite.lib import cros_build_lib
+from chromite.lib.paygen import paygen_payload_lib
 from chromite.service import payload
 
 
@@ -17,6 +19,10 @@
                       ('full_update', 'tgt_unsigned_image'),
                       ('full_update', 'tgt_signed_image'),
                       ('full_update', 'tgt_dlc_image'))
+_VALID_MINIOS_PAIRS = (('src_signed_image', 'tgt_signed_image'),
+                       ('src_unsigned_image', 'tgt_unsigned_image'),
+                       ('full_update', 'tgt_unsigned_image'),
+                       ('full_update', 'tgt_signed_image'))
 
 _DEFAULT_PAYGEN_CACHE_DIR = '.paygen_cache'
 
@@ -63,13 +69,19 @@
     cros_build_lib.Die('%s and %s are not valid image pairs' %
                        (src_image, tgt_image))
 
+  # Ensure that miniOS payloads are only requested for compatible image types.
+  if input_proto.minios and (src_name, tgt_name) not in _VALID_MINIOS_PAIRS:
+    cros_build_lib.Die('%s and %s are not valid image pairs for miniOS' %
+                       (src_image, tgt_image))
+
   # Find the value of bucket or default to 'chromeos-releases'.
   destination_bucket = input_proto.bucket or 'chromeos-releases'
 
   # There's a potential that some paygen_lib library might raise here, but since
   # we're still involved in config we'll keep it before the validate_only.
   payload_config = payload.PayloadConfig(tgt_image, src_image,
-                                         destination_bucket, input_proto.verify,
+                                         destination_bucket, input_proto.minios,
+                                         input_proto.verify,
                                          upload=not input_proto.dryrun,
                                          cache_dir=_DEFAULT_PAYGEN_CACHE_DIR)
 
@@ -78,9 +90,16 @@
     return controller.RETURN_CODE_VALID_INPUT
 
   # Do payload generation.
-  local_path, remote_uri = payload_config.GeneratePayload()
-  _SetGeneratePayloadOutputProto(output_proto, local_path, remote_uri)
+  local_path, remote_uri = '', ''
+  try:
+    local_path, remote_uri = payload_config.GeneratePayload()
+  except paygen_payload_lib.PayloadGenerationSkippedException as e:
+    # If paygen was skipped, provide a reason if possible.
+    if isinstance(e, paygen_payload_lib.NoMiniOSPartitionException):
+      reason = payload_pb2.GenerationResponse.NOT_MINIOS_COMPATIBLE
+      output_proto.failure_reason = reason
 
+  _SetGeneratePayloadOutputProto(output_proto, local_path, remote_uri)
   if remote_uri or input_proto.dryrun and local_path:
     return controller.RETURN_CODE_SUCCESS
   else: