pushimage: push keyset loading down to allow for input flexibility

Sometimes we do want sub-artifacts to sign with a different keyset, like
when a firmware builder also produces accessory firmware.  The current
code will only check the keysets from the main file and then ignore any
setting in the sub-file.  Instead, delay the keyset loading to after we
know the image type so we can pull it from the specific input file.

BUG=chrome-os-partner:46635
TEST=`./cbuildbot/run_tests` passes
TEST=`./pushimage -n gs://foo/ --board smaug --debug` with custom input insns shows correct output insns

Change-Id: I8ed170e43b6e2c115d39dfdc772fbc93054cc9d7
Reviewed-on: https://chromium-review.googlesource.com/311280
Commit-Ready: Mike Frysinger <vapier@chromium.org>
Tested-by: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
diff --git a/scripts/pushimage_unittest.py b/scripts/pushimage_unittest.py
index 9f5dcf1..ac9b492 100644
--- a/scripts/pushimage_unittest.py
+++ b/scripts/pushimage_unittest.py
@@ -62,8 +62,8 @@
   def testOutputInsnsBasic(self):
     """Verify output instructions are sane"""
     exp_content = """[insns]
-keyset = stumpy-mp-v3
 channel = dev canary
+keyset = stumpy-mp-v3
 chromeos_shell = false
 ensure_no_password = true
 firmware_update = true
@@ -75,7 +75,7 @@
 
     insns = pushimage.InputInsns('test.board')
     m = self.PatchObject(osutils, 'WriteFile')
-    insns.OutputInsns('recovery', '/bogus', {}, {})
+    insns.OutputInsns('/bogus', {}, {})
     self.assertTrue(m.called)
     content = m.call_args_list[0][0][1]
     self.assertEqual(content.rstrip(), exp_content.rstrip())
@@ -83,8 +83,8 @@
   def testOutputInsnsReplacements(self):
     """Verify output instructions can be updated"""
     exp_content = """[insns]
-keyset = batman
 channel = dev
+keyset = batman
 chromeos_shell = false
 ensure_no_password = true
 firmware_update = true
@@ -106,7 +106,7 @@
 
     insns = pushimage.InputInsns('test.board')
     m = self.PatchObject(osutils, 'WriteFile')
-    insns.OutputInsns('recovery', '/a/file', sect_insns, sect_general)
+    insns.OutputInsns('/a/file', sect_insns, sect_general)
     self.assertTrue(m.called)
     content = m.call_args_list[0][0][1]
     self.assertEqual(content.rstrip(), exp_content.rstrip())
@@ -271,6 +271,31 @@
       self.assertRaises(pushimage.PushError, pushimage.PushImage, '/src',
                         'test.board', 'R34-5126.0.0')
 
+  def testMultipleKeysets(self):
+    """Verify behavior when processing an insn w/multiple keysets"""
+    EXPECTED = {
+        'canary': [
+            ('gs://chromeos-releases/canary-channel/test.board/5126.0.0/'
+             'ChromeOS-recovery-R34-5126.0.0-test.board.instructions'),
+            ('gs://chromeos-releases/canary-channel/test.board/5126.0.0/'
+             'ChromeOS-recovery-R34-5126.0.0-test.board-key2.instructions'),
+            ('gs://chromeos-releases/canary-channel/test.board/5126.0.0/'
+             'ChromeOS-recovery-R34-5126.0.0-test.board-key3.instructions'),
+        ],
+        'dev': [
+            ('gs://chromeos-releases/dev-channel/test.board/5126.0.0/'
+             'ChromeOS-recovery-R34-5126.0.0-test.board.instructions'),
+            ('gs://chromeos-releases/dev-channel/test.board/5126.0.0/'
+             'ChromeOS-recovery-R34-5126.0.0-test.board-key2.instructions'),
+            ('gs://chromeos-releases/dev-channel/test.board/5126.0.0/'
+             'ChromeOS-recovery-R34-5126.0.0-test.board-key3.instructions'),
+        ],
+    }
+    with mock.patch.object(gs.GSContext, 'Exists', return_value=True):
+      urls = pushimage.PushImage('/src', 'test.board', 'R34-5126.0.0',
+                                 force_keysets=('key1', 'key2', 'key3'))
+    self.assertEqual(urls, EXPECTED)
+
 
 class MainTests(cros_test_lib.MockTestCase):
   """Tests for main()"""
@@ -289,4 +314,4 @@
   signing.INPUT_INSN_DIR = signing.TEST_INPUT_INSN_DIR
 
   # Run the tests.
-  cros_test_lib.main(level='info', module=__name__)
+  cros_test_lib.main(level='notice', module=__name__)