blob: ae0ac6e3fe54ca1dd0a70f58bb5dd2cd0e8f70f8 [file] [log] [blame]
Mike Frysingerd13faeb2013-09-05 16:00:46 -04001# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""ChromeOS image pusher (from cbuildbot to signer).
6
7This pushes files from the archive bucket to the signer bucket and marks
8artifacts for signing (which a signing process will look for).
9"""
10
11from __future__ import print_function
12
13import ConfigParser
14import cStringIO
Mike Frysingerd13faeb2013-09-05 16:00:46 -040015import getpass
16import os
17import re
18import tempfile
Mike Frysinger09fe0122014-02-09 02:44:05 -050019import textwrap
Mike Frysingerd13faeb2013-09-05 16:00:46 -040020
Don Garrett88b8d782014-05-13 17:30:55 -070021from chromite.cbuildbot import constants
Mike Frysingerd13faeb2013-09-05 16:00:46 -040022from chromite.lib import commandline
23from chromite.lib import cros_build_lib
Ralph Nathan5a582ff2015-03-20 18:18:30 -070024from chromite.lib import cros_logging as logging
Mike Frysingerd13faeb2013-09-05 16:00:46 -040025from chromite.lib import gs
26from chromite.lib import osutils
27from chromite.lib import signing
28
29
30# This will split a fully qualified ChromeOS version string up.
31# R34-5126.0.0 will break into "34" and "5126.0.0".
32VERSION_REGEX = r'^R([0-9]+)-([^-]+)'
33
Mike Frysingerdad40d62014-02-09 02:18:02 -050034# The test signers will scan this dir looking for test work.
35# Keep it in sync with the signer config files [gs_test_buckets].
36TEST_SIGN_BUCKET_BASE = 'gs://chromeos-throw-away-bucket/signer-tests'
37
38# Ketsets that are only valid in the above test bucket.
39TEST_KEYSETS = set(('test-keys-mp', 'test-keys-premp'))
40
Amey Deshpande01c965a2015-08-12 17:27:54 -070041# Supported image types for signing.
42_SUPPORTED_IMAGE_TYPES = (
43 constants.IMAGE_TYPE_RECOVERY,
44 constants.IMAGE_TYPE_FACTORY,
45 constants.IMAGE_TYPE_FIRMWARE,
46 constants.IMAGE_TYPE_BASE,
47)
48
Mike Frysingerd13faeb2013-09-05 16:00:46 -040049
Mike Frysinger4495b032014-03-05 17:24:03 -050050class PushError(Exception):
51 """When an (unknown) error happened while trying to push artifacts."""
52
53
Mike Frysingerd13faeb2013-09-05 16:00:46 -040054class MissingBoardInstructions(Exception):
55 """Raised when a board lacks any signer instructions."""
56
57
58class InputInsns(object):
59 """Object to hold settings for a signable board.
60
61 Note: The format of the instruction file pushimage outputs (and the signer
62 reads) is not exactly the same as the instruction file pushimage reads.
63 """
64
65 def __init__(self, board):
66 self.board = board
67
68 config = ConfigParser.ConfigParser()
69 config.readfp(open(self.GetInsnFile('DEFAULT')))
Amey Deshpande01c965a2015-08-12 17:27:54 -070070 # What pushimage internally refers to as 'recovery', are the basic signing
71 # instructions in practice, and other types are stacked on top.
72 input_insns = self.GetInsnFile(constants.IMAGE_TYPE_RECOVERY)
73 if not os.path.exists(input_insns):
74 # This board doesn't have any signing instructions.
75 raise MissingBoardInstructions(self.board)
76 config.readfp(open(input_insns))
Mike Frysingerd13faeb2013-09-05 16:00:46 -040077 self.cfg = config
78
79 def GetInsnFile(self, image_type):
80 """Find the signer instruction files for this board/image type.
81
82 Args:
83 image_type: The type of instructions to load. It can be a common file
84 (like "DEFAULT"), or one of the --sign-types.
85
86 Returns:
87 Full path to the instruction file using |image_type| and |self.board|.
88 """
89 if image_type == image_type.upper():
90 name = image_type
Amey Deshpande01c965a2015-08-12 17:27:54 -070091 elif image_type in (constants.IMAGE_TYPE_RECOVERY,
92 constants.IMAGE_TYPE_BASE):
Mike Frysingerd13faeb2013-09-05 16:00:46 -040093 name = self.board
94 else:
95 name = '%s.%s' % (self.board, image_type)
96
97 return os.path.join(signing.INPUT_INSN_DIR, '%s.instructions' % name)
98
99 @staticmethod
100 def SplitCfgField(val):
101 """Split a string into multiple elements.
102
103 This centralizes our convention for multiple elements in the input files
104 being delimited by either a space or comma.
105
106 Args:
107 val: The string to split.
108
109 Returns:
110 The list of elements from having done split the string.
111 """
112 return val.replace(',', ' ').split()
113
114 def GetChannels(self):
115 """Return the list of channels to sign for this board.
116
117 If the board-specific config doesn't specify a preference, we'll use the
118 common settings.
119 """
120 return self.SplitCfgField(self.cfg.get('insns', 'channel'))
121
122 def GetKeysets(self):
123 """Return the list of keysets to sign for this board."""
124 return self.SplitCfgField(self.cfg.get('insns', 'keyset'))
125
126 def OutputInsns(self, image_type, output_file, sect_insns, sect_general):
127 """Generate the output instruction file for sending to the signer.
128
129 Note: The format of the instruction file pushimage outputs (and the signer
130 reads) is not exactly the same as the instruction file pushimage reads.
131
132 Args:
133 image_type: The type of image we will be signing (see --sign-types).
134 output_file: The file to write the new instruction file to.
135 sect_insns: Items to set/override in the [insns] section.
136 sect_general: Items to set/override in the [general] section.
137 """
138 config = ConfigParser.ConfigParser()
139 config.readfp(open(self.GetInsnFile(image_type)))
140
141 # Clear channel entry in instructions file, ensuring we only get
142 # one channel for the signer to look at. Then provide all the
143 # other details for this signing request to avoid any ambiguity
144 # and to avoid relying on encoding data into filenames.
145 for sect, fields in zip(('insns', 'general'), (sect_insns, sect_general)):
146 if not config.has_section(sect):
147 config.add_section(sect)
148 for k, v in fields.iteritems():
149 config.set(sect, k, v)
150
151 output = cStringIO.StringIO()
152 config.write(output)
153 data = output.getvalue()
154 osutils.WriteFile(output_file, data)
Ralph Nathan5a582ff2015-03-20 18:18:30 -0700155 logging.debug('generated insns file for %s:\n%s', image_type, data)
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400156
157
158def MarkImageToBeSigned(ctx, tbs_base, insns_path, priority):
159 """Mark an instructions file for signing.
160
161 This will upload a file to the GS bucket flagging an image for signing by
162 the signers.
163
164 Args:
165 ctx: A viable gs.GSContext.
166 tbs_base: The full path to where the tobesigned directory lives.
167 insns_path: The path (relative to |tbs_base|) of the file to sign.
168 priority: Set the signing priority (lower == higher prio).
169
170 Returns:
171 The full path to the remote tobesigned file.
172 """
173 if priority < 0 or priority > 99:
174 raise ValueError('priority must be [0, 99] inclusive')
175
176 if insns_path.startswith(tbs_base):
177 insns_path = insns_path[len(tbs_base):].lstrip('/')
178
179 tbs_path = '%s/tobesigned/%02i,%s' % (tbs_base, priority,
180 insns_path.replace('/', ','))
181
Mike Frysinger6430d132014-10-27 23:43:30 -0400182 # The caller will catch gs.GSContextException for us.
183 ctx.Copy('-', tbs_path, input=cros_build_lib.MachineDetails())
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400184
185 return tbs_path
186
187
188def PushImage(src_path, board, versionrev=None, profile=None, priority=50,
Mike Frysingerdad40d62014-02-09 02:18:02 -0500189 sign_types=None, dry_run=False, mock=False, force_keysets=()):
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400190 """Push the image from the archive bucket to the release bucket.
191
192 Args:
193 src_path: Where to copy the files from; can be a local path or gs:// URL.
194 Should be a full path to the artifacts in either case.
195 board: The board we're uploading artifacts for (e.g. $BOARD).
196 versionrev: The full Chromium OS version string (e.g. R34-5126.0.0).
197 profile: The board profile in use (e.g. "asan").
198 priority: Set the signing priority (lower == higher prio).
199 sign_types: If set, a set of types which we'll restrict ourselves to
200 signing. See the --sign-types option for more details.
201 dry_run: Show what would be done, but do not upload anything.
202 mock: Upload to a testing bucket rather than the real one.
Mike Frysingerdad40d62014-02-09 02:18:02 -0500203 force_keysets: Set of keysets to use rather than what the inputs say.
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400204
205 Returns:
Don Garrett9459c2f2014-01-22 18:20:24 -0800206 A dictionary that maps 'channel' -> ['gs://signer_instruction_uri1',
207 'gs://signer_instruction_uri2',
208 ...]
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400209 """
Mike Frysinger4495b032014-03-05 17:24:03 -0500210 # Whether we hit an unknown error. If so, we'll throw an error, but only
211 # at the end (so that we still upload as many files as possible).
Amey Deshpande01c965a2015-08-12 17:27:54 -0700212 # It's implemented using a list to deal with variable scopes in nested
213 # functions below.
214 unknown_error = [False]
Mike Frysinger4495b032014-03-05 17:24:03 -0500215
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400216 if versionrev is None:
217 # Extract milestone/version from the directory name.
218 versionrev = os.path.basename(src_path)
219
220 # We only support the latest format here. Older releases can use pushimage
221 # from the respective branch which deals with legacy cruft.
222 m = re.match(VERSION_REGEX, versionrev)
223 if not m:
224 raise ValueError('version %s does not match %s' %
225 (versionrev, VERSION_REGEX))
226 milestone = m.group(1)
227 version = m.group(2)
228
229 # Normalize board to always use dashes not underscores. This is mostly a
230 # historical artifact at this point, but we can't really break it since the
231 # value is used in URLs.
232 boardpath = board.replace('_', '-')
233 if profile is not None:
234 boardpath += '-%s' % profile.replace('_', '-')
235
236 ctx = gs.GSContext(dry_run=dry_run)
237
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400238 try:
239 input_insns = InputInsns(board)
240 except MissingBoardInstructions as e:
Ralph Nathan446aee92015-03-23 14:44:56 -0700241 logging.warning('board "%s" is missing base instruction file: %s', board, e)
242 logging.warning('not uploading anything for signing')
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400243 return
244 channels = input_insns.GetChannels()
Mike Frysingerdad40d62014-02-09 02:18:02 -0500245
246 # We want force_keysets as a set, and keysets as a list.
247 force_keysets = set(force_keysets)
248 keysets = list(force_keysets) if force_keysets else input_insns.GetKeysets()
249
250 if mock:
Ralph Nathan03047282015-03-23 11:09:32 -0700251 logging.info('Upload mode: mock; signers will not process anything')
Mike Frysingerdad40d62014-02-09 02:18:02 -0500252 tbs_base = gs_base = os.path.join(constants.TRASH_BUCKET, 'pushimage-tests',
253 getpass.getuser())
254 elif TEST_KEYSETS & force_keysets:
Ralph Nathan03047282015-03-23 11:09:32 -0700255 logging.info('Upload mode: test; signers will process test keys')
Mike Frysingerdad40d62014-02-09 02:18:02 -0500256 # We need the tbs_base to be in the place the signer will actually scan.
257 tbs_base = TEST_SIGN_BUCKET_BASE
258 gs_base = os.path.join(tbs_base, getpass.getuser())
259 else:
Ralph Nathan03047282015-03-23 11:09:32 -0700260 logging.info('Upload mode: normal; signers will process the images')
Mike Frysingerdad40d62014-02-09 02:18:02 -0500261 tbs_base = gs_base = constants.RELEASE_BUCKET
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400262
263 sect_general = {
264 'config_board': board,
265 'board': boardpath,
266 'version': version,
267 'versionrev': versionrev,
268 'milestone': milestone,
269 }
270 sect_insns = {}
271
272 if dry_run:
Ralph Nathan03047282015-03-23 11:09:32 -0700273 logging.info('DRY RUN MODE ACTIVE: NOTHING WILL BE UPLOADED')
274 logging.info('Signing for channels: %s', ' '.join(channels))
275 logging.info('Signing for keysets : %s', ' '.join(keysets))
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400276
Don Garrett9459c2f2014-01-22 18:20:24 -0800277 instruction_urls = {}
278
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400279 def _ImageNameBase(image_type=None):
280 lmid = ('%s-' % image_type) if image_type else ''
281 return 'ChromeOS-%s%s-%s' % (lmid, versionrev, boardpath)
282
Amey Deshpande01c965a2015-08-12 17:27:54 -0700283 # These variables are defined outside the loop so that the nested functions
284 # below can access them without 'cell-var-from-loop' linter warning.
285 dst_path = ""
286 files_to_sign = []
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400287 for channel in channels:
Ralph Nathan5a582ff2015-03-20 18:18:30 -0700288 logging.debug('\n\n#### CHANNEL: %s ####\n', channel)
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400289 sect_insns['channel'] = channel
290 sub_path = '%s-channel/%s/%s' % (channel, boardpath, version)
291 dst_path = '%s/%s' % (gs_base, sub_path)
Ralph Nathan03047282015-03-23 11:09:32 -0700292 logging.info('Copying images to %s', dst_path)
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400293
Amey Deshpande01c965a2015-08-12 17:27:54 -0700294 recovery_basename = _ImageNameBase(constants.IMAGE_TYPE_RECOVERY)
295 factory_basename = _ImageNameBase(constants.IMAGE_TYPE_FACTORY)
296 firmware_basename = _ImageNameBase(constants.IMAGE_TYPE_FIRMWARE)
297 test_basename = _ImageNameBase(constants.IMAGE_TYPE_TEST)
298 base_basename = _ImageNameBase(constants.IMAGE_TYPE_BASE)
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400299 hwqual_tarball = 'chromeos-hwqual-%s-%s.tar.bz2' % (board, versionrev)
300
Amey Deshpande01c965a2015-08-12 17:27:54 -0700301 # The following build artifacts, if present, are always copied regardless of
302 # requested signing types.
303 files_to_copy_only = (
304 # (<src>, <dst>, <suffix>),
305 ('image.zip', _ImageNameBase(), 'zip'),
306 (constants.TEST_IMAGE_TAR, test_basename, 'tar.xz'),
307 ('debug.tgz', 'debug-%s' % boardpath, 'tgz'),
308 (hwqual_tarball, '', ''),
309 ('au-generator.zip', '', ''),
310 ('stateful.tgz', '', ''),
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400311 )
Amey Deshpande01c965a2015-08-12 17:27:54 -0700312
313 # The following build artifacts, if present, are always copied.
314 # If |sign_types| is None, all of them are marked for signing, otherwise
315 # only the image types specified in |sign_types| are marked for signing.
316 files_to_copy_and_maybe_sign = (
317 # (<src>, <dst>, <suffix>, <signing type>),
318 (constants.RECOVERY_IMAGE_TAR, recovery_basename, 'tar.xz',
319 constants.IMAGE_TYPE_RECOVERY),
320
321 ('factory_image.zip', factory_basename, 'zip',
322 constants.IMAGE_TYPE_FACTORY),
323
324 ('firmware_from_source.tar.bz2', firmware_basename, 'tar.bz2',
325 constants.IMAGE_TYPE_FIRMWARE),
326 )
327
328 # The following build artifacts are copied and marked for signing, if
329 # they are present *and* if the image type is specified via |sign_types|.
330 files_to_maybe_copy_and_sign = (
331 # (<src>, <dst>, <suffix>, <signing type>),
332 (constants.BASE_IMAGE_TAR, base_basename, 'tar.xz',
333 constants.IMAGE_TYPE_BASE),
334 )
335
336 def _CopyFileToGS(src, dst, suffix):
337 """Returns |dst| file name if the copying was successful."""
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400338 if not dst:
339 dst = src
Amey Deshpande01c965a2015-08-12 17:27:54 -0700340 elif suffix:
341 dst = '%s.%s' % (dst, suffix)
342 success = False
Mike Frysingere51a2652014-01-18 02:36:16 -0500343 try:
344 ctx.Copy(os.path.join(src_path, src), os.path.join(dst_path, dst))
Amey Deshpande01c965a2015-08-12 17:27:54 -0700345 success = True
Mike Frysingere51a2652014-01-18 02:36:16 -0500346 except gs.GSNoSuchKey:
Ralph Nathan446aee92015-03-23 14:44:56 -0700347 logging.warning('Skipping %s as it does not exist', src)
Mike Frysinger4495b032014-03-05 17:24:03 -0500348 except gs.GSContextException:
Amey Deshpande01c965a2015-08-12 17:27:54 -0700349 unknown_error[0] = True
Ralph Nathan59900422015-03-24 10:41:17 -0700350 logging.error('Skipping %s due to unknown GS error', src, exc_info=True)
Amey Deshpande01c965a2015-08-12 17:27:54 -0700351 return dst if success else None
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400352
Amey Deshpande01c965a2015-08-12 17:27:54 -0700353 for src, dst, suffix in files_to_copy_only:
354 _CopyFileToGS(src, dst, suffix)
355
356 # Clear the list of files to sign before adding new artifacts.
357 files_to_sign = []
358
359 def _AddToFilesToSign(image_type, dst, suffix):
360 # We cannot use os.path.splitext() here because the archive file name
361 # has '.<board>' prior to the file extension.
362 dst_base = dst.rstrip(suffix).rstrip('.')
363 assert dst == '%s.%s' % (dst_base, suffix), (
364 'dst: %s, dst_base: %s, suffix: %s' % (dst, dst_base, suffix))
365 files_to_sign.append([image_type, dst_base, suffix])
366
367 for src, dst, suffix, image_type in files_to_copy_and_maybe_sign:
368 dst = _CopyFileToGS(src, dst, suffix)
369 if dst and (sign_types is None or image_type in sign_types):
370 _AddToFilesToSign(image_type, dst, suffix)
371
372 for src, dst, suffix, image_type in files_to_maybe_copy_and_sign:
373 if sign_types and image_type in sign_types:
374 dst = _CopyFileToGS(src, dst, suffix)
375 if dst:
376 _AddToFilesToSign(image_type, dst, suffix)
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400377
378 # Now go through the subset for signing.
379 for keyset in keysets:
Ralph Nathan5a582ff2015-03-20 18:18:30 -0700380 logging.debug('\n\n#### KEYSET: %s ####\n', keyset)
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400381 sect_insns['keyset'] = keyset
382 for image_type, dst_name, suffix in files_to_sign:
Amey Deshpande01c965a2015-08-12 17:27:54 -0700383 dst_archive = '%s.%s' % (dst_name, suffix)
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400384 sect_general['archive'] = dst_archive
385 sect_general['type'] = image_type
386
Amey Deshpande01c965a2015-08-12 17:27:54 -0700387 # In the default/automatic mode, only flag files for signing if the
388 # archives were actually uploaded in a previous stage. This additional
389 # check can be removed in future once |sign_types| becomes a required
390 # argument.
391 # TODO: Make |sign_types| a required argument.
392 gs_artifact_path = os.path.join(dst_path, dst_archive)
393 exists = False
394 try:
395 exists = ctx.Exists(gs_artifact_path)
396 except gs.GSContextException:
397 unknown_error[0] = True
398 logging.error('Unknown error while checking %s', gs_artifact_path,
399 exc_info=True)
400 if not exists:
401 logging.info('%s does not exist. Nothing to sign.',
402 gs_artifact_path)
403 continue
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400404
405 input_insn_path = input_insns.GetInsnFile(image_type)
406 if not os.path.exists(input_insn_path):
Ralph Nathan03047282015-03-23 11:09:32 -0700407 logging.info('%s does not exist. Nothing to sign.', input_insn_path)
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400408 continue
409
410 # Generate the insn file for this artifact that the signer will use,
411 # and flag it for signing.
412 with tempfile.NamedTemporaryFile(
413 bufsize=0, prefix='pushimage.insns.') as insns_path:
414 input_insns.OutputInsns(image_type, insns_path.name, sect_insns,
415 sect_general)
416
417 gs_insns_path = '%s/%s' % (dst_path, dst_name)
418 if keyset != keysets[0]:
419 gs_insns_path += '-%s' % keyset
420 gs_insns_path += '.instructions'
421
Mike Frysinger4495b032014-03-05 17:24:03 -0500422 try:
423 ctx.Copy(insns_path.name, gs_insns_path)
424 except gs.GSContextException:
Amey Deshpande01c965a2015-08-12 17:27:54 -0700425 unknown_error[0] = True
Ralph Nathan59900422015-03-24 10:41:17 -0700426 logging.error('Unknown error while uploading insns %s',
427 gs_insns_path, exc_info=True)
Mike Frysinger4495b032014-03-05 17:24:03 -0500428 continue
429
430 try:
431 MarkImageToBeSigned(ctx, tbs_base, gs_insns_path, priority)
432 except gs.GSContextException:
Amey Deshpande01c965a2015-08-12 17:27:54 -0700433 unknown_error[0] = True
Ralph Nathan59900422015-03-24 10:41:17 -0700434 logging.error('Unknown error while marking for signing %s',
435 gs_insns_path, exc_info=True)
Mike Frysinger4495b032014-03-05 17:24:03 -0500436 continue
Ralph Nathan03047282015-03-23 11:09:32 -0700437 logging.info('Signing %s image %s', image_type, gs_insns_path)
Don Garrett9459c2f2014-01-22 18:20:24 -0800438 instruction_urls.setdefault(channel, []).append(gs_insns_path)
439
Amey Deshpande01c965a2015-08-12 17:27:54 -0700440 if unknown_error[0]:
Mike Frysinger4495b032014-03-05 17:24:03 -0500441 raise PushError('hit some unknown error(s)', instruction_urls)
442
Don Garrett9459c2f2014-01-22 18:20:24 -0800443 return instruction_urls
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400444
445
446def main(argv):
447 parser = commandline.ArgumentParser(description=__doc__)
448
449 # The type of image_dir will strip off trailing slashes (makes later
450 # processing simpler and the display prettier).
451 parser.add_argument('image_dir', default=None, type='local_or_gs_path',
452 help='full path of source artifacts to upload')
453 parser.add_argument('--board', default=None, required=True,
454 help='board to generate symbols for')
455 parser.add_argument('--profile', default=None,
456 help='board profile in use (e.g. "asan")')
457 parser.add_argument('--version', default=None,
458 help='version info (normally extracted from image_dir)')
459 parser.add_argument('-n', '--dry-run', default=False, action='store_true',
460 help='show what would be done, but do not upload')
461 parser.add_argument('-M', '--mock', default=False, action='store_true',
462 help='upload things to a testing bucket (dev testing)')
Mike Frysingerdad40d62014-02-09 02:18:02 -0500463 parser.add_argument('--test-sign-mp', default=False, action='store_true',
464 help='mung signing behavior to sign w/test mp keys')
465 parser.add_argument('--test-sign-premp', default=False, action='store_true',
466 help='mung signing behavior to sign w/test premp keys')
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400467 parser.add_argument('--priority', type=int, default=50,
468 help='set signing priority (lower == higher prio)')
469 parser.add_argument('--sign-types', default=None, nargs='+',
Amey Deshpande01c965a2015-08-12 17:27:54 -0700470 choices=_SUPPORTED_IMAGE_TYPES,
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400471 help='only sign specified image types')
Mike Frysinger09fe0122014-02-09 02:44:05 -0500472 parser.add_argument('--yes', action='store_true', default=False,
473 help='answer yes to all prompts')
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400474
475 opts = parser.parse_args(argv)
476 opts.Freeze()
477
Mike Frysingerdad40d62014-02-09 02:18:02 -0500478 force_keysets = set()
479 if opts.test_sign_mp:
480 force_keysets.add('test-keys-mp')
481 if opts.test_sign_premp:
482 force_keysets.add('test-keys-premp')
483
Mike Frysinger09fe0122014-02-09 02:44:05 -0500484 # If we aren't using mock or test or dry run mode, then let's prompt the user
485 # to make sure they actually want to do this. It's rare that people want to
486 # run this directly and hit the release bucket.
487 if not (opts.mock or force_keysets or opts.dry_run) and not opts.yes:
488 prolog = '\n'.join(textwrap.wrap(textwrap.dedent(
489 'Uploading images for signing to the *release* bucket is not something '
490 'you generally should be doing yourself.'), 80)).strip()
491 if not cros_build_lib.BooleanPrompt(
492 prompt='Are you sure you want to sign these images',
493 default=False, prolog=prolog):
494 cros_build_lib.Die('better safe than sorry')
495
Mike Frysingerd13faeb2013-09-05 16:00:46 -0400496 PushImage(opts.image_dir, opts.board, versionrev=opts.version,
497 profile=opts.profile, priority=opts.priority,
Mike Frysingerdad40d62014-02-09 02:18:02 -0500498 sign_types=opts.sign_types, dry_run=opts.dry_run, mock=opts.mock,
499 force_keysets=force_keysets)