diff options
Diffstat (limited to 'third_party/libwebrtc/build/fuchsia/pkg_repo.py')
-rw-r--r-- | third_party/libwebrtc/build/fuchsia/pkg_repo.py | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/third_party/libwebrtc/build/fuchsia/pkg_repo.py b/third_party/libwebrtc/build/fuchsia/pkg_repo.py new file mode 100644 index 0000000000..3e635e9ec0 --- /dev/null +++ b/third_party/libwebrtc/build/fuchsia/pkg_repo.py @@ -0,0 +1,209 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import common +import json +import logging +import os +import shutil +import subprocess +import tempfile +import time + +from six.moves import urllib + +# Maximum amount of time to block while waiting for "pm serve" to come up. +_PM_SERVE_LIVENESS_TIMEOUT_SECS = 10 + +_MANAGED_REPO_NAME = 'chrome-runner' + + +class PkgRepo(object): + """Abstract interface for a repository used to serve packages to devices.""" + + def __init__(self, target): + self._target = target + + def PublishPackage(self, package_path): + pm_tool = common.GetHostToolPathFromPlatform('pm') + # Flags for `pm publish`: + # https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/src/sys/pkg/bin/pm/cmd/pm/publish/publish.go + # https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/src/sys/pkg/bin/pm/repo/config.go + # -a: Publish archived package + # -f <path>: Path to packages + # -r <path>: Path to repository + # -vt: Repo versioning based on time rather than monotonic version number + # increase + # -v: Verbose output + subprocess.check_call([ + pm_tool, 'publish', '-a', '-f', package_path, '-r', + self.GetPath(), '-vt', '-v' + ], + stderr=subprocess.STDOUT) + + def GetPath(self): + pass + + +class ManagedPkgRepo(PkgRepo): + """Creates and serves packages from an ephemeral repository.""" + + def __init__(self, target): + PkgRepo.__init__(self, target) + self._with_count = 0 + + self._pkg_root = tempfile.mkdtemp() + pm_tool = common.GetHostToolPathFromPlatform('pm') + subprocess.check_call([pm_tool, 'newrepo', '-repo', self._pkg_root]) + logging.info('Creating and serving temporary package root: {}.'.format( + self._pkg_root)) + + serve_port = common.GetAvailableTcpPort() + # Flags for `pm serve`: + # https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/src/sys/pkg/bin/pm/cmd/pm/serve/serve.go + # -l <port>: Port to listen on + # -c 2: Use config.json format v2, the default for pkgctl + # -q: Don't print out information about requests + self._pm_serve_task = subprocess.Popen([ + pm_tool, 'serve', '-d', + os.path.join(self._pkg_root, 'repository'), '-l', + ':%d' % serve_port, '-c', '2', '-q' + ]) + + # Block until "pm serve" starts serving HTTP traffic at |serve_port|. + timeout = time.time() + _PM_SERVE_LIVENESS_TIMEOUT_SECS + while True: + try: + urllib.request.urlopen('http://localhost:%d' % serve_port, + timeout=1).read() + break + except urllib.error.URLError: + logging.info('Waiting until \'pm serve\' is up...') + + if time.time() >= timeout: + raise Exception('Timed out while waiting for \'pm serve\'.') + + time.sleep(1) + + remote_port = common.ConnectPortForwardingTask(target, serve_port, 0) + self._RegisterPkgRepository(self._pkg_root, remote_port) + + def __enter__(self): + self._with_count += 1 + return self + + def __exit__(self, type, value, tb): + # Allows the repository to delete itself when it leaves the scope of a 'with' block. + self._with_count -= 1 + if self._with_count > 0: + return + + self._UnregisterPkgRepository() + self._pm_serve_task.kill() + self._pm_serve_task = None + + logging.info('Cleaning up package root: ' + self._pkg_root) + shutil.rmtree(self._pkg_root) + self._pkg_root = None + + def GetPath(self): + return self._pkg_root + + def _RegisterPkgRepository(self, tuf_repo, remote_port): + """Configures a device to use a local TUF repository as an installation + source for packages. + |tuf_repo|: The host filesystem path to the TUF repository. + |remote_port|: The reverse-forwarded port used to connect to instance of + `pm serve` that is serving the contents of |tuf_repo|.""" + + # Extract the public signing key for inclusion in the config file. + root_keys = [] + root_json_path = os.path.join(tuf_repo, 'repository', 'root.json') + root_json = json.load(open(root_json_path, 'r')) + for root_key_id in root_json['signed']['roles']['root']['keyids']: + root_keys.append({ + 'type': + root_json['signed']['keys'][root_key_id]['keytype'], + 'value': + root_json['signed']['keys'][root_key_id]['keyval']['public'] + }) + + # "pm serve" can automatically generate a "config.json" file at query time, + # but the file is unusable because it specifies URLs with port + # numbers that are unreachable from across the port forwarding boundary. + # So instead, we generate our own config file with the forwarded port + # numbers instead. + config_file = open(os.path.join(tuf_repo, 'repository', 'repo_config.json'), + 'w') + json.dump( + { + 'repo_url': + "fuchsia-pkg://%s" % _MANAGED_REPO_NAME, + 'root_keys': + root_keys, + 'mirrors': [{ + "mirror_url": "http://127.0.0.1:%d" % remote_port, + "subscribe": True + }], + 'root_threshold': + 1, + 'root_version': + 1 + }, config_file) + config_file.close() + + # Register the repo. + return_code = self._target.RunCommand([ + ('pkgctl repo rm fuchsia-pkg://%s; ' + + 'pkgctl repo add url http://127.0.0.1:%d/repo_config.json; ') % + (_MANAGED_REPO_NAME, remote_port) + ]) + if return_code != 0: + raise Exception('Error code %d when running pkgctl repo add.' % + return_code) + + rule_template = """'{"version":"1","content":[{"host_match":"fuchsia.com","host_replacement":"%s","path_prefix_match":"/","path_prefix_replacement":"/"}]}'""" + return_code = self._target.RunCommand([ + ('pkgctl rule replace json %s') % (rule_template % (_MANAGED_REPO_NAME)) + ]) + if return_code != 0: + raise Exception('Error code %d when running pkgctl rule replace.' % + return_code) + + def _UnregisterPkgRepository(self): + """Unregisters the package repository.""" + + logging.debug('Unregistering package repository.') + self._target.RunCommand( + ['pkgctl', 'repo', 'rm', + 'fuchsia-pkg://%s' % (_MANAGED_REPO_NAME)]) + + # Re-enable 'devhost' repo if it's present. This is useful for devices that + # were booted with 'fx serve'. + self._target.RunCommand([ + 'pkgctl', 'rule', 'replace', 'json', + """'{"version":"1","content":[{"host_match":"fuchsia.com","host_replacement":"devhost","path_prefix_match":"/","path_prefix_replacement":"/"}]}'""" + ], + silent=True) + + +class ExternalPkgRepo(PkgRepo): + """Publishes packages to a package repository located and served externally + (ie. located under a Fuchsia build directory and served by "fx serve".""" + + def __init__(self, pkg_root): + self._pkg_root = pkg_root + logging.info('Using existing package root: {}'.format(pkg_root)) + logging.info( + 'ATTENTION: This will not start a package server. Please run "fx serve" manually.' + ) + + def GetPath(self): + return self._pkg_root + + def __enter__(self): + return self + + def __exit__(self, type, value, tb): + pass |