beeps | bff9f9d | 2013-12-06 11:14:08 -0800 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | # Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
| 6 | import httplib2 |
| 7 | import json |
beeps | 023afc6 | 2014-02-04 16:59:22 -0800 | [diff] [blame^] | 8 | import logging |
beeps | bff9f9d | 2013-12-06 11:14:08 -0800 | [diff] [blame] | 9 | import os |
| 10 | import re |
| 11 | import shutil |
| 12 | import urllib2 |
| 13 | |
| 14 | import common |
| 15 | |
beeps | f860bd1 | 2014-01-23 18:06:06 -0800 | [diff] [blame] | 16 | from autotest_lib.client.common_lib import autotemp |
beeps | bff9f9d | 2013-12-06 11:14:08 -0800 | [diff] [blame] | 17 | from autotest_lib.client.common_lib import utils |
| 18 | |
beeps | bff9f9d | 2013-12-06 11:14:08 -0800 | [diff] [blame] | 19 | |
| 20 | TEST_EXTENSION_ID = 'hfaagokkkhdbgiakmmlclaapfelnkoah' |
| 21 | UPDATE_CHECK_URL = ('https://clients2.google.com/service/update2/') |
| 22 | UPDATE_CHECK_PARAMETER = ('crx?x=id%%3D%s%%26v%%3D0%%26uc') |
| 23 | MANIFEST_KEY = ('MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+hlN5FB+tjCsBszmBIvI' |
| 24 | 'cD/djLLQm2zZfFygP4U4/o++ZM91EWtgII10LisoS47qT2TIOg4Un4+G57e' |
| 25 | 'lZ9PjEIhcJfANqkYrD3t9dpEzMNr936TLB2u683B5qmbB68Nq1Eel7KVc+F' |
| 26 | '0BqhBondDqhvDvGPEV0vBsbErJFlNH7SQIDAQAB') |
| 27 | |
| 28 | |
beeps | f860bd1 | 2014-01-23 18:06:06 -0800 | [diff] [blame] | 29 | class SonicDownloaderException(Exception): |
| 30 | """Generic sonic dowloader exception.""" |
| 31 | pass |
| 32 | |
| 33 | |
beeps | bff9f9d | 2013-12-06 11:14:08 -0800 | [diff] [blame] | 34 | def get_download_url_from_omaha(extension_id): |
| 35 | """Retrieves an update url from omaha for the specified extension id. |
| 36 | |
| 37 | @param extension_id: The extension id of the chromecast extension. |
| 38 | |
| 39 | @return: A url to download the extension from. |
| 40 | |
| 41 | @raises IOError: If the response returned by the omaha server is invalid. |
| 42 | """ |
| 43 | update_check_link = '%s%s' % (UPDATE_CHECK_URL, |
| 44 | UPDATE_CHECK_PARAMETER % extension_id) |
| 45 | response_xml = httplib2.Http().request(update_check_link, 'GET')[1] |
| 46 | codebase_match = re.compile(r'codebase="(.*crx)"').search(response_xml) |
| 47 | if codebase_match is not None: |
beeps | 023afc6 | 2014-02-04 16:59:22 -0800 | [diff] [blame^] | 48 | logging.info('Omaha response while downloading extension: %s', |
| 49 | response_xml) |
beeps | bff9f9d | 2013-12-06 11:14:08 -0800 | [diff] [blame] | 50 | return codebase_match.groups()[0] |
| 51 | raise IOError('Omaha response is invalid %s.' % response_xml) |
| 52 | |
| 53 | |
| 54 | def download_extension(dest_file): |
| 55 | """Retrieve the extension into a destination crx file. |
| 56 | |
| 57 | @param dest_file: Path to a destination file for the extension. |
| 58 | """ |
| 59 | download_url = get_download_url_from_omaha(TEST_EXTENSION_ID) |
beeps | 023afc6 | 2014-02-04 16:59:22 -0800 | [diff] [blame^] | 60 | logging.info('Downloading extension from %s', download_url) |
beeps | bff9f9d | 2013-12-06 11:14:08 -0800 | [diff] [blame] | 61 | response = urllib2.urlopen(download_url) |
| 62 | with open(dest_file, 'w') as f: |
| 63 | f.write(response.read()) |
| 64 | |
| 65 | |
| 66 | def fix_public_key(extracted_extension_folder): |
| 67 | """Modifies the manifest.json to include a public key. |
| 68 | |
| 69 | This function will erase the content in the original manifest |
| 70 | and replace it with a new manifest that contains the key. |
| 71 | |
| 72 | @param extracted_extension_folder: The folder containing |
| 73 | the extracted extension. |
| 74 | """ |
| 75 | manifest_json_file = os.path.join(extracted_extension_folder, |
| 76 | 'manifest.json') |
| 77 | with open(manifest_json_file, 'r') as f: |
| 78 | manifest_json = json.loads(f.read()) |
| 79 | |
| 80 | manifest_json['key'] = MANIFEST_KEY |
| 81 | |
| 82 | with open(manifest_json_file, 'w') as f: |
| 83 | f.write(json.dumps(manifest_json)) |
| 84 | |
| 85 | |
beeps | f860bd1 | 2014-01-23 18:06:06 -0800 | [diff] [blame] | 86 | def setup_extension(unzipped_crx_dir): |
beeps | bff9f9d | 2013-12-06 11:14:08 -0800 | [diff] [blame] | 87 | """Setup for tests that need a chromecast extension. |
| 88 | |
| 89 | Download the extension from an omaha server, unzip it and modify its |
| 90 | manifest.json to include a public key. |
| 91 | |
beeps | f860bd1 | 2014-01-23 18:06:06 -0800 | [diff] [blame] | 92 | @param unzipped_crx_dir: Destination directory for the unzipped extension. |
beeps | bff9f9d | 2013-12-06 11:14:08 -0800 | [diff] [blame] | 93 | |
| 94 | @raises CmdTimeoutError: If we timeout unzipping the extension. |
| 95 | """ |
beeps | f860bd1 | 2014-01-23 18:06:06 -0800 | [diff] [blame] | 96 | output_crx_dir = autotemp.tempdir() |
| 97 | output_crx = os.path.join(output_crx_dir.name, 'sonic_extension.crx') |
| 98 | try: |
| 99 | download_extension(output_crx) |
| 100 | unzip_cmd = 'unzip -o "%s" -d "%s"' % (output_crx, unzipped_crx_dir) |
beeps | bff9f9d | 2013-12-06 11:14:08 -0800 | [diff] [blame] | 101 | |
beeps | f860bd1 | 2014-01-23 18:06:06 -0800 | [diff] [blame] | 102 | # The unzip command will return a non-zero exit status if there are |
| 103 | # extra bytes at the start/end of the zipfile. This is not a critical |
| 104 | # failure and the extension will still work. |
| 105 | cmd_output = utils.run(unzip_cmd, ignore_status=True, timeout=1) |
| 106 | except Exception as e: |
| 107 | if os.path.exists(unzipped_crx_dir): |
| 108 | shutil.rmtree() |
| 109 | raise SonicDownloaderException(e) |
| 110 | finally: |
| 111 | if os.path.exists(output_crx): |
| 112 | os.remove(output_crx) |
| 113 | output_crx_dir.clean() |
| 114 | |
beeps | bff9f9d | 2013-12-06 11:14:08 -0800 | [diff] [blame] | 115 | if not os.path.exists(unzipped_crx_dir): |
beeps | f860bd1 | 2014-01-23 18:06:06 -0800 | [diff] [blame] | 116 | raise SonicDownloaderException('Unable to download sonic extension.') |
beeps | 023afc6 | 2014-02-04 16:59:22 -0800 | [diff] [blame^] | 117 | logging.info('Sonic extension successfully downloaded into %s.', |
| 118 | unzipped_crx_dir) |
beeps | bff9f9d | 2013-12-06 11:14:08 -0800 | [diff] [blame] | 119 | |
| 120 | # TODO(beeps): crbug.com/325869, investigate the limits of component |
| 121 | # extensions. For now this is ok because even sonic testing inlines a |
| 122 | # public key for their test extension. |
beeps | f860bd1 | 2014-01-23 18:06:06 -0800 | [diff] [blame] | 123 | try: |
| 124 | fix_public_key(unzipped_crx_dir) |
| 125 | except Exception as e: |
| 126 | shutil.rmtree(unzipped_crx_dir) |
| 127 | raise SonicDownloaderException(e) |