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_unittest.py b/api/controller/payload_unittest.py
index 217facc..b1c260c 100644
--- a/api/controller/payload_unittest.py
+++ b/api/controller/payload_unittest.py
@@ -41,13 +41,22 @@
         keyset='update_signer',
         dryrun=False)
 
+    self.minios_req = payload_pb2.GenerationRequest(
+        tgt_unsigned_image=tgt_image,
+        src_unsigned_image=src_image,
+        bucket='test-destination-bucket',
+        minios=True,
+        verify=True,
+        keyset='update_signer',
+        dryrun=False)
+
     self.result = payload_pb2.GenerationResponse(
         success=True,
         local_path='/tmp/aohiwdadoi/delta.bin',
         remote_uri='gs://something')
 
   def testValidateOnly(self):
-    """Sanity check that a validate only call does not execute any logic."""
+    """Basic check that a validate only call does not execute any logic."""
 
     res = payload.GeneratePayload(self.req, self.result,
                                   self.validate_only_config)
@@ -78,3 +87,18 @@
                                   self.mock_call_config)
     patch.assert_not_called()
     self.assertEqual(controller.RETURN_CODE_SUCCESS, res)
+
+  def testMiniOSSuccess(self):
+    """Test a miniOS paygen request."""
+    patch = self.PatchObject(paygen_payload_lib, 'PaygenPayload')
+    patch.return_value.Run.return_value = 'gs://minios/something'
+    res = payload.GeneratePayload(self.minios_req, self.result, self.api_config)
+    self.assertEqual(res, controller.RETURN_CODE_SUCCESS)
+
+  def testNoMiniOSPartition(self):
+    """Test a miniOS paygen request on an image with no miniOS part."""
+    patch = self.PatchObject(paygen_payload_lib, 'PaygenPayload')
+    patch.side_effect = paygen_payload_lib.NoMiniOSPartitionException
+    payload.GeneratePayload(self.minios_req, self.result, self.api_config)
+    self.assertEqual(self.result.failure_reason,
+                     payload_pb2.GenerationResponse.NOT_MINIOS_COMPATIBLE)