summaryrefslogtreecommitdiff
path: root/scripts/pushimage.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/pushimage.py')
-rw-r--r--scripts/pushimage.py256
1 files changed, 182 insertions, 74 deletions
diff --git a/scripts/pushimage.py b/scripts/pushimage.py
index 58c0ca23c..342295f34 100644
--- a/scripts/pushimage.py
+++ b/scripts/pushimage.py
@@ -49,6 +49,8 @@ _SUPPORTED_IMAGE_TYPES = (
constants.IMAGE_TYPE_FACTORY,
constants.IMAGE_TYPE_FIRMWARE,
constants.IMAGE_TYPE_NV_LP0_FIRMWARE,
+ constants.IMAGE_TYPE_ACCESSORY_USBPD,
+ constants.IMAGE_TYPE_ACCESSORY_RWSIG,
constants.IMAGE_TYPE_BASE,
)
@@ -60,6 +62,10 @@ class PushError(Exception):
class MissingBoardInstructions(Exception):
"""Raised when a board lacks any signer instructions."""
+ def __init__(self, board, image_type, input_insns):
+ Exception.__init__(self, 'Board %s lacks insns for %s image: %s not found' %
+ (board, image_type, input_insns))
+
class InputInsns(object):
"""Object to hold settings for a signable board.
@@ -68,18 +74,38 @@ class InputInsns(object):
reads) is not exactly the same as the instruction file pushimage reads.
"""
- def __init__(self, board):
+ def __init__(self, board, image_type=None):
+ """Initialization.
+
+ Args:
+ board: The board to look up details.
+ image_type: The type of image we will be signing (see --sign-types).
+ """
self.board = board
config = ConfigParser.ConfigParser()
config.readfp(open(self.GetInsnFile('DEFAULT')))
+
# What pushimage internally refers to as 'recovery', are the basic signing
# instructions in practice, and other types are stacked on top.
+ if image_type is None:
+ image_type = constants.IMAGE_TYPE_RECOVERY
+ self.image_type = image_type
input_insns = self.GetInsnFile(constants.IMAGE_TYPE_RECOVERY)
if not os.path.exists(input_insns):
# This board doesn't have any signing instructions.
- raise MissingBoardInstructions(self.board)
+ raise MissingBoardInstructions(self.board, image_type, input_insns)
config.readfp(open(input_insns))
+
+ if image_type is not None:
+ input_insns = self.GetInsnFile(image_type)
+ if not os.path.exists(input_insns):
+ # This type doesn't have any signing instructions.
+ raise MissingBoardInstructions(self.board, image_type, input_insns)
+
+ self.image_type = image_type
+ config.readfp(open(input_insns))
+
self.cfg = config
def GetInsnFile(self, image_type):
@@ -125,24 +151,82 @@ class InputInsns(object):
"""
return self.SplitCfgField(self.cfg.get('insns', 'channel'))
- def GetKeysets(self):
- """Return the list of keysets to sign for this board."""
- return self.SplitCfgField(self.cfg.get('insns', 'keyset'))
+ def GetKeysets(self, insns_merge=None):
+ """Return the list of keysets to sign for this board.
+
+ Args:
+ insns_merge: The additional section to look at over [insns].
+ """
+ # First load the default value from [insns.keyset] if available.
+ sections = ['insns']
+ # Then overlay the [insns.xxx.keyset] if requested.
+ if insns_merge is not None:
+ sections += [insns_merge]
+
+ keyset = ''
+ for section in sections:
+ try:
+ keyset = self.cfg.get(section, 'keyset')
+ except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
+ pass
+
+ # We do not perturb the order (e.g. using sorted() or making a set())
+ # because we want the behavior stable, and we want the input insns to
+ # explicitly control the order (since it has an impact on naming).
+ return self.SplitCfgField(keyset)
+
+ def GetAltInsnSets(self):
+ """Return the list of alternative insn sections."""
+ # We do not perturb the order (e.g. using sorted() or making a set())
+ # because we want the behavior stable, and we want the input insns to
+ # explicitly control the order (since it has an impact on naming).
+ ret = [x for x in self.cfg.sections() if x.startswith('insns.')]
+ return ret if ret else [None]
+
+ @staticmethod
+ def CopyConfigParser(config):
+ """Return a copy of a ConfigParser object.
+
+ The python guys broke the ability to use something like deepcopy:
+ https://bugs.python.org/issue16058
+ """
+ # Write the current config to a string io object.
+ data = cStringIO.StringIO()
+ config.write(data)
+ data.seek(0)
+
+ # Create a new ConfigParser from the serialized data.
+ ret = ConfigParser.ConfigParser()
+ ret.readfp(data)
+
+ return ret
- def OutputInsns(self, image_type, output_file, sect_insns, sect_general):
+ def OutputInsns(self, output_file, sect_insns, sect_general,
+ insns_merge=None):
"""Generate the output instruction file for sending to the signer.
+ The override order is (later has precedence):
+ [insns]
+ [insns_merge] (should be named "insns.xxx")
+ sect_insns
+
Note: The format of the instruction file pushimage outputs (and the signer
reads) is not exactly the same as the instruction file pushimage reads.
Args:
- image_type: The type of image we will be signing (see --sign-types).
output_file: The file to write the new instruction file to.
sect_insns: Items to set/override in the [insns] section.
sect_general: Items to set/override in the [general] section.
+ insns_merge: The alternative insns.xxx section to merge.
"""
- config = ConfigParser.ConfigParser()
- config.readfp(open(self.GetInsnFile(image_type)))
+ # Create a copy so we can clobber certain fields.
+ config = self.CopyConfigParser(self.cfg)
+ sect_insns = sect_insns.copy()
+
+ # Merge in the alternative insns section if need be.
+ if insns_merge is not None:
+ for k, v in config.items(insns_merge):
+ sect_insns.setdefault(k, v)
# Clear channel entry in instructions file, ensuring we only get
# one channel for the signer to look at. Then provide all the
@@ -154,11 +238,15 @@ class InputInsns(object):
for k, v in fields.iteritems():
config.set(sect, k, v)
+ # Now prune the alternative sections.
+ for alt in self.GetAltInsnSets():
+ config.remove_section(alt)
+
output = cStringIO.StringIO()
config.write(output)
data = output.getvalue()
osutils.WriteFile(output_file, data)
- logging.debug('generated insns file for %s:\n%s', image_type, data)
+ logging.debug('generated insns file for %s:\n%s', self.image_type, data)
def MarkImageToBeSigned(ctx, tbs_base, insns_path, priority):
@@ -244,14 +332,13 @@ def PushImage(src_path, board, versionrev=None, profile=None, priority=50,
try:
input_insns = InputInsns(board)
except MissingBoardInstructions as e:
- logging.warning('board "%s" is missing base instruction file: %s', board, e)
+ logging.warning('Missing base instruction file: %s', e)
logging.warning('not uploading anything for signing')
return
channels = input_insns.GetChannels()
- # We want force_keysets as a set, and keysets as a list.
+ # We want force_keysets as a set.
force_keysets = set(force_keysets)
- keysets = list(force_keysets) if force_keysets else input_insns.GetKeysets()
if mock:
logging.info('Upload mode: mock; signers will not process anything')
@@ -279,7 +366,6 @@ def PushImage(src_path, board, versionrev=None, profile=None, priority=50,
if dry_run:
logging.info('DRY RUN MODE ACTIVE: NOTHING WILL BE UPLOADED')
logging.info('Signing for channels: %s', ' '.join(channels))
- logging.info('Signing for keysets : %s', ' '.join(keysets))
instruction_urls = {}
@@ -303,6 +389,8 @@ def PushImage(src_path, board, versionrev=None, profile=None, priority=50,
firmware_basename = _ImageNameBase(constants.IMAGE_TYPE_FIRMWARE)
nv_lp0_firmware_basename = _ImageNameBase(
constants.IMAGE_TYPE_NV_LP0_FIRMWARE)
+ acc_usbpd_basename = _ImageNameBase(constants.IMAGE_TYPE_ACCESSORY_USBPD)
+ acc_rwsig_basename = _ImageNameBase(constants.IMAGE_TYPE_ACCESSORY_RWSIG)
test_basename = _ImageNameBase(constants.IMAGE_TYPE_TEST)
base_basename = _ImageNameBase(constants.IMAGE_TYPE_BASE)
hwqual_tarball = 'chromeos-hwqual-%s-%s.tar.bz2' % (board, versionrev)
@@ -335,6 +423,12 @@ def PushImage(src_path, board, versionrev=None, profile=None, priority=50,
('firmware_from_source.tar.bz2', nv_lp0_firmware_basename, 'tar.bz2',
constants.IMAGE_TYPE_NV_LP0_FIRMWARE),
+
+ ('firmware_from_source.tar.bz2', acc_usbpd_basename, 'tar.bz2',
+ constants.IMAGE_TYPE_ACCESSORY_USBPD),
+
+ ('firmware_from_source.tar.bz2', acc_rwsig_basename, 'tar.bz2',
+ constants.IMAGE_TYPE_ACCESSORY_RWSIG),
)
# The following build artifacts are copied and marked for signing, if
@@ -387,66 +481,80 @@ def PushImage(src_path, board, versionrev=None, profile=None, priority=50,
logging.debug('Files to sign: %s', files_to_sign)
# Now go through the subset for signing.
- for keyset in keysets:
- logging.debug('\n\n#### KEYSET: %s ####\n', keyset)
- sect_insns['keyset'] = keyset
- for image_type, dst_name, suffix in files_to_sign:
- dst_archive = '%s.%s' % (dst_name, suffix)
- sect_general['archive'] = dst_archive
- sect_general['type'] = image_type
-
- # In the default/automatic mode, only flag files for signing if the
- # archives were actually uploaded in a previous stage. This additional
- # check can be removed in future once |sign_types| becomes a required
- # argument.
- # TODO: Make |sign_types| a required argument.
- gs_artifact_path = os.path.join(dst_path, dst_archive)
- exists = False
- try:
- exists = ctx.Exists(gs_artifact_path)
- except gs.GSContextException:
- unknown_error[0] = True
- logging.error('Unknown error while checking %s', gs_artifact_path,
- exc_info=True)
- if not exists:
- logging.info('%s does not exist. Nothing to sign.',
- gs_artifact_path)
- continue
-
- input_insn_path = input_insns.GetInsnFile(image_type)
- if not os.path.exists(input_insn_path):
- logging.info('%s does not exist. Nothing to sign.', input_insn_path)
- continue
-
- # Generate the insn file for this artifact that the signer will use,
- # and flag it for signing.
- with tempfile.NamedTemporaryFile(
- bufsize=0, prefix='pushimage.insns.') as insns_path:
- input_insns.OutputInsns(image_type, insns_path.name, sect_insns,
- sect_general)
-
- gs_insns_path = '%s/%s' % (dst_path, dst_name)
- if keyset != keysets[0]:
- gs_insns_path += '-%s' % keyset
- gs_insns_path += '.instructions'
-
- try:
- ctx.Copy(insns_path.name, gs_insns_path)
- except gs.GSContextException:
- unknown_error[0] = True
- logging.error('Unknown error while uploading insns %s',
- gs_insns_path, exc_info=True)
- continue
-
- try:
- MarkImageToBeSigned(ctx, tbs_base, gs_insns_path, priority)
- except gs.GSContextException:
- unknown_error[0] = True
- logging.error('Unknown error while marking for signing %s',
- gs_insns_path, exc_info=True)
- continue
- logging.info('Signing %s image %s', image_type, gs_insns_path)
- instruction_urls.setdefault(channel, []).append(gs_insns_path)
+ for image_type, dst_name, suffix in files_to_sign:
+ try:
+ input_insns = InputInsns(board, image_type=image_type)
+ except MissingBoardInstructions as e:
+ logging.info('Nothing to sign: %s', e)
+ continue
+
+ dst_archive = '%s.%s' % (dst_name, suffix)
+ sect_general['archive'] = dst_archive
+ sect_general['type'] = image_type
+
+ # In the default/automatic mode, only flag files for signing if the
+ # archives were actually uploaded in a previous stage. This additional
+ # check can be removed in future once |sign_types| becomes a required
+ # argument.
+ # TODO: Make |sign_types| a required argument.
+ gs_artifact_path = os.path.join(dst_path, dst_archive)
+ exists = False
+ try:
+ exists = ctx.Exists(gs_artifact_path)
+ except gs.GSContextException:
+ unknown_error[0] = True
+ logging.error('Unknown error while checking %s', gs_artifact_path,
+ exc_info=True)
+ if not exists:
+ logging.info('%s does not exist. Nothing to sign.',
+ gs_artifact_path)
+ continue
+
+ first_image = True
+ for alt_insn_set in input_insns.GetAltInsnSets():
+ # Figure out which keysets have been requested for this type.
+ # We sort the forced set so tests/runtime behavior is stable.
+ keysets = sorted(force_keysets)
+ if not keysets:
+ keysets = input_insns.GetKeysets(insns_merge=alt_insn_set)
+ if not keysets:
+ logging.warning('Skipping %s image signing due to no keysets',
+ image_type)
+
+ for keyset in keysets:
+ sect_insns['keyset'] = keyset
+
+ # Generate the insn file for this artifact that the signer will use,
+ # and flag it for signing.
+ with tempfile.NamedTemporaryFile(
+ bufsize=0, prefix='pushimage.insns.') as insns_path:
+ input_insns.OutputInsns(insns_path.name, sect_insns, sect_general,
+ insns_merge=alt_insn_set)
+
+ gs_insns_path = '%s/%s' % (dst_path, dst_name)
+ if not first_image:
+ gs_insns_path += '-%s' % keyset
+ first_image = False
+ gs_insns_path += '.instructions'
+
+ try:
+ ctx.Copy(insns_path.name, gs_insns_path)
+ except gs.GSContextException:
+ unknown_error[0] = True
+ logging.error('Unknown error while uploading insns %s',
+ gs_insns_path, exc_info=True)
+ continue
+
+ try:
+ MarkImageToBeSigned(ctx, tbs_base, gs_insns_path, priority)
+ except gs.GSContextException:
+ unknown_error[0] = True
+ logging.error('Unknown error while marking for signing %s',
+ gs_insns_path, exc_info=True)
+ continue
+ logging.info('Signing %s image with keyset %s at %s', image_type,
+ keyset, gs_insns_path)
+ instruction_urls.setdefault(channel, []).append(gs_insns_path)
if unknown_error[0]:
raise PushError('hit some unknown error(s)', instruction_urls)