pushimage: add support for handling base image

'pushimage' currently assumes the image to be signed is of 'recovery' type
if no type was specified.  It can handle firmware and factory image
types if the corresponding instructions files are present.

With this change, pushimage can handle 'base' image type.  Specifically, it
copies base images to signers and flags them for singing if the
'base' image type is specified via '--sign-types' commandline flag.

This change does not change the default behavior when '--sign-types' is
not specified at commandline.

This patch is a revised version of CL:292987, which was reverted
(CL:296837 and chromium:527277).

BUG=chromium:512940
BUG=chromium:529427
TEST=unittests

Change-Id: I8ad304dac9b04dcc8045c93abc68955e8e76a840
Reviewed-on: https://chromium-review.googlesource.com/297305
Commit-Ready: Amey Deshpande <ameyd@google.com>
Tested-by: Amey Deshpande <ameyd@google.com>
Reviewed-by: Aseda Aboagye <aaboagye@chromium.org>
Reviewed-by: Aviv Keshet <akeshet@chromium.org>
diff --git a/scripts/pushimage_unittest.py b/scripts/pushimage_unittest.py
index 5edbd53..a634417 100644
--- a/scripts/pushimage_unittest.py
+++ b/scripts/pushimage_unittest.py
@@ -188,6 +188,21 @@
 
     self.assertEqual(urls, EXPECTED)
 
+  def testBasic_RealBoardName(self):
+    """Runs a simple smoke test using a real board name."""
+    EXPECTED = {
+        'canary': [
+            ('gs://chromeos-releases/canary-channel/x86-alex/5126.0.0/'
+             'ChromeOS-recovery-R34-5126.0.0-x86-alex.instructions')],
+        'dev': [
+            ('gs://chromeos-releases/dev-channel/x86-alex/5126.0.0/'
+             'ChromeOS-recovery-R34-5126.0.0-x86-alex.instructions')],
+    }
+    with mock.patch.object(gs.GSContext, 'Exists', return_value=True):
+      urls = pushimage.PushImage('/src', 'x86-alex', 'R34-5126.0.0')
+
+    self.assertEqual(urls, EXPECTED)
+
   def testBasicMock(self):
     """Simple smoke test in mock mode"""
     with mock.patch.object(gs.GSContext, 'Exists', return_value=True):
@@ -215,12 +230,31 @@
              'ChromeOS-recovery-R34-5126.0.0-test.board.instructions')],
     }
 
-    urls = pushimage.PushImage('/src', 'test.board', 'R34-5126.0.0',
-                               sign_types=['recovery'])
+    with mock.patch.object(gs.GSContext, 'Exists', return_value=True):
+      urls = pushimage.PushImage('/src', 'test.board', 'R34-5126.0.0',
+                                 sign_types=['recovery'])
     self.assertEqual(self.gs_mock.call_count, 20)
     self.assertTrue(self.mark_mock.called)
     self.assertEqual(urls, EXPECTED)
 
+  def testSignTypesBase(self):
+    """Only sign the requested recovery type"""
+    EXPECTED = {
+        'canary': [
+            ('gs://chromeos-releases/canary-channel/test.board/5126.0.0/'
+             'ChromeOS-base-R34-5126.0.0-test.board.instructions')],
+        'dev': [
+            ('gs://chromeos-releases/dev-channel/test.board/5126.0.0/'
+             'ChromeOS-base-R34-5126.0.0-test.board.instructions')],
+    }
+
+    with mock.patch.object(gs.GSContext, 'Exists', return_value=True):
+      urls = pushimage.PushImage('/src', 'test.board', 'R34-5126.0.0',
+                                 sign_types=['base'])
+    self.assertEqual(self.gs_mock.call_count, 22)
+    self.assertTrue(self.mark_mock.called)
+    self.assertEqual(urls, EXPECTED)
+
   def testSignTypesNone(self):
     """Verify nothing is signed when we request an unavailable type"""
     urls = pushimage.PushImage('/src', 'test.board', 'R34-5126.0.0',