зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1506912 - Raptor support for tp6 pageload on android geckoview; r=jmaher
Differential Revision: https://phabricator.services.mozilla.com/D14186 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
e1336d9a63
Коммит
a63f5d5395
|
@ -10,7 +10,7 @@ playback_cls = {
|
|||
}
|
||||
|
||||
|
||||
def get_playback(config):
|
||||
def get_playback(config, android_device=None):
|
||||
tool_name = config.get('playback_tool', None)
|
||||
if tool_name is None:
|
||||
LOG.critical("playback_tool name not found in config")
|
||||
|
@ -20,4 +20,4 @@ def get_playback(config):
|
|||
return None
|
||||
|
||||
cls = playback_cls.get(tool_name)
|
||||
return cls(config)
|
||||
return cls(config, android_device)
|
||||
|
|
|
@ -11,8 +11,9 @@ from abc import ABCMeta, abstractmethod
|
|||
class Playback(object):
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
def __init__(self, config):
|
||||
def __init__(self, config, android_device=None):
|
||||
self.config = config
|
||||
self.android_device = android_device
|
||||
|
||||
@abstractmethod
|
||||
def download(self):
|
||||
|
|
|
@ -90,12 +90,13 @@ POLICIES_CONTENT_OFF = '''{
|
|||
|
||||
class Mitmproxy(Playback, Python3Virtualenv, TestingMixin, MercurialScript):
|
||||
|
||||
def __init__(self, config):
|
||||
def __init__(self, config, android_device=None):
|
||||
self.config = config
|
||||
self.mitmproxy_proc = None
|
||||
self.mitmdump_path = None
|
||||
self.recordings = config.get('playback_recordings', None)
|
||||
self.browser_path = config.get('binary', None)
|
||||
self.android_device = android_device
|
||||
|
||||
# raptor_dir is where we will download all mitmproxy required files
|
||||
# when running locally it comes from obj_path via mozharness/mach
|
||||
|
@ -202,9 +203,15 @@ class Mitmproxy(Playback, Python3Virtualenv, TestingMixin, MercurialScript):
|
|||
# for google chromium this is not necessary as chromium will be
|
||||
# started with --ignore-certificate-errors cmd line arg
|
||||
if self.config['app'] == "firefox":
|
||||
# install the generated CA certificate into Firefox
|
||||
self.install_mitmproxy_cert(self.mitmproxy_proc,
|
||||
self.browser_path)
|
||||
# install the generated CA certificate into Firefox desktop
|
||||
self.install_mitmproxy_cert_desktop(self.mitmproxy_proc,
|
||||
self.browser_path)
|
||||
elif self.config['app'] == "geckoview":
|
||||
# install the generated CA certificate into android geckoview
|
||||
self.install_mitmproxy_cert_android(self.mitmproxy_proc,
|
||||
self.browser_path)
|
||||
else:
|
||||
return
|
||||
|
||||
def start(self):
|
||||
# if on windows, the mitmdump_path was already set when creating py3 env
|
||||
|
@ -223,19 +230,14 @@ class Mitmproxy(Playback, Python3Virtualenv, TestingMixin, MercurialScript):
|
|||
self.turn_off_browser_proxy()
|
||||
return
|
||||
|
||||
def install_mitmproxy_cert(self, mitmproxy_proc, browser_path):
|
||||
def install_mitmproxy_cert_desktop(self, mitmproxy_proc, browser_path):
|
||||
"""Install the CA certificate generated by mitmproxy, into Firefox
|
||||
1. Create a directory called distribution in the same directory as the Firefox executable
|
||||
2. Create a file called policies.json with:
|
||||
{
|
||||
"policies": {
|
||||
"certificates": {
|
||||
"Install": ["FULL_PATH_TO_CERT"]
|
||||
}
|
||||
}
|
||||
}
|
||||
1. Create a dir called 'distribution' in the same directory as the Firefox executable
|
||||
2. Create the policies.json file inside that folder; which points to the certificate
|
||||
location, and turns on the the browser proxy settings
|
||||
"""
|
||||
LOG.info("Installing mitmproxy CA certficate into Firefox")
|
||||
|
||||
# browser_path is the exe, we want the folder
|
||||
self.policies_dir = os.path.dirname(browser_path)
|
||||
# on macosx we need to remove the last folders 'MacOS'
|
||||
|
@ -262,11 +264,68 @@ class Mitmproxy(Playback, Python3Virtualenv, TestingMixin, MercurialScript):
|
|||
'host': self.config['host']})
|
||||
|
||||
# cannot continue if failed to add CA cert to Firefox, need to check
|
||||
if not self.is_mitmproxy_cert_installed():
|
||||
LOG.error('Aborting: failed to install mitmproxy CA cert into Firefox')
|
||||
if not self.is_mitmproxy_cert_installed_desktop():
|
||||
LOG.error('Aborting: failed to install mitmproxy CA cert into Firefox desktop')
|
||||
self.stop_mitmproxy_playback()
|
||||
sys.exit()
|
||||
|
||||
def install_mitmproxy_cert_android(self, mitmproxy_proc, browser_path):
|
||||
"""Install the CA certificate generated by mitmproxy, into geckoview android
|
||||
1. Get the `certutil` tool.
|
||||
2. Create an NSS certificate database in the geckoview browser profile dir.
|
||||
`certutil -N -d sql:<path to profile> --empty-password`
|
||||
3. Import the mitmproxy certificate into the database.
|
||||
`certutil -A -d sql:<path to profile> -n "some nickname" -t TC,, -a -i <path to CA.pem>`
|
||||
"""
|
||||
# get the certutil tool
|
||||
if self.config.get("obj_path", None) is not None:
|
||||
# when running locally, it is found in the Firefox desktop build (..obj../dist/bin)
|
||||
self.certutil = os.path.join(self.config['obj_path'], 'dist', 'bin')
|
||||
else:
|
||||
# in production it is already downloaded on the host automation machines via hostutils
|
||||
# self.certutil = TODO
|
||||
LOG.info("TODO: where is the path in production to certutil/hostutils?")
|
||||
|
||||
bin_suffix = mozinfo.info.get('bin_suffix', '')
|
||||
self.certutil = os.path.join(self.certutil, "certutil" + bin_suffix)
|
||||
|
||||
if os.path.isfile(self.certutil):
|
||||
LOG.info("certutil is found at: %s" % self.certutil)
|
||||
else:
|
||||
LOG.critical("unable to find certutil at %s" % self.certutil)
|
||||
|
||||
# DEFAULT_CERT_PATH has local path and name of mitmproxy cert i.e.
|
||||
# /home/cltbld/.mitmproxy/mitmproxy-ca-cert.cer
|
||||
self.local_cert_path = DEFAULT_CERT_PATH
|
||||
|
||||
# create cert db
|
||||
param1 = "sql:%s/" % self.config['local_profile_dir']
|
||||
command = [self.certutil, '-N', '-d', param1, '--empty-password']
|
||||
|
||||
LOG.info("creating nss cert database using command: %s" % ' '.join(command))
|
||||
cmd_proc = subprocess.Popen(command, env=os.environ.copy())
|
||||
time.sleep(3)
|
||||
cmd_terminated = cmd_proc.poll()
|
||||
if cmd_terminated is None: # None value indicates process hasn't terminated
|
||||
LOG.critical("nss cert db creation command failed to complete")
|
||||
|
||||
# import mitmproxy cert into the db
|
||||
command = [self.certutil, '-A', '-d', param1, '-n',
|
||||
'mitmproxy-cert', '-t', 'TC,,', '-a', '-i', self.local_cert_path]
|
||||
|
||||
LOG.info("importing mitmproxy cert into db using command: %s" % ' '.join(command))
|
||||
cmd_proc = subprocess.Popen(command, env=os.environ.copy())
|
||||
time.sleep(3)
|
||||
cmd_terminated = cmd_proc.poll()
|
||||
if cmd_terminated is None: # None value indicates process hasn't terminated
|
||||
LOG.critical("command to import mitmproxy cert into cert db failed to complete")
|
||||
|
||||
# cannot continue if failed to add CA cert to Firefox, need to check
|
||||
# if not self.is_mitmproxy_cert_installed_android():
|
||||
# LOG.error('Aborting: failed to install mitmproxy CA cert into Firefox')
|
||||
# self.stop_mitmproxy_playback()
|
||||
# sys.exit()
|
||||
|
||||
def write_policies_json(self, location, policies_content):
|
||||
policies_file = os.path.join(location, "policies.json")
|
||||
LOG.info("writing: %s" % policies_file)
|
||||
|
@ -281,7 +340,7 @@ class Mitmproxy(Playback, Python3Virtualenv, TestingMixin, MercurialScript):
|
|||
with open(policies_file, 'r') as fd:
|
||||
return fd.read()
|
||||
|
||||
def is_mitmproxy_cert_installed(self):
|
||||
def is_mitmproxy_cert_installed_desktop(self):
|
||||
"""Verify mitmxproy CA cert was added to Firefox"""
|
||||
try:
|
||||
# read autoconfig file, confirm mitmproxy cert is in there
|
||||
|
@ -300,6 +359,11 @@ class Mitmproxy(Playback, Python3Virtualenv, TestingMixin, MercurialScript):
|
|||
return False
|
||||
return True
|
||||
|
||||
def is_mitmproxy_cert_installed_android(self):
|
||||
"""Verify mitmxproy CA cert was added to Firefox"""
|
||||
LOG.info("* TODO: verify cert is installed on android")
|
||||
return False
|
||||
|
||||
def start_mitmproxy_playback(self,
|
||||
mitmdump_path,
|
||||
mitmproxy_recording_path,
|
||||
|
|
|
@ -28,8 +28,6 @@ else:
|
|||
mozharness_dir = os.path.join(here, '../../../mozharness')
|
||||
sys.path.insert(0, mozharness_dir)
|
||||
|
||||
from mozharness.mozilla.firefox.autoconfig import _cfg_file_path, _autoconfig_path
|
||||
|
||||
webext_dir = os.path.join(os.path.dirname(here), 'webext')
|
||||
sys.path.insert(0, here)
|
||||
|
||||
|
@ -50,16 +48,6 @@ from results import RaptorResultsHandler
|
|||
from gecko_profile import GeckoProfile
|
||||
|
||||
|
||||
def remove_autoconfig(binary):
|
||||
bindir = os.path.dirname(binary)
|
||||
mozillacfg = _cfg_file_path(bindir)
|
||||
if os.path.isfile(mozillacfg):
|
||||
os.unlink(mozillacfg)
|
||||
autoconfig = _autoconfig_path(bindir)
|
||||
if os.path.isfile(autoconfig):
|
||||
os.unlink(autoconfig)
|
||||
|
||||
|
||||
class Raptor(object):
|
||||
"""Container class for Raptor"""
|
||||
|
||||
|
@ -86,6 +74,7 @@ class Raptor(object):
|
|||
self.benchmark = None
|
||||
self.gecko_profiler = None
|
||||
self.post_startup_delay = 30000
|
||||
self.device = None
|
||||
|
||||
# debug mode is currently only supported when running locally
|
||||
self.debug_mode = debug_mode if self.config['run_local'] else False
|
||||
|
@ -101,9 +90,6 @@ class Raptor(object):
|
|||
self.profile = create_profile('firefox')
|
||||
else:
|
||||
self.profile = create_profile(self.config['app'])
|
||||
# Clear any existing mozilla.cfg file to prevent earlier
|
||||
# runs using mitmproxy from interfering with settings.
|
||||
remove_autoconfig(binary)
|
||||
|
||||
# Merge in base profiles
|
||||
with open(os.path.join(self.profile_data_dir, 'profiles.json'), 'r') as fh:
|
||||
|
@ -114,6 +100,9 @@ class Raptor(object):
|
|||
self.log.info("Merging profile: {}".format(path))
|
||||
self.profile.merge(path)
|
||||
|
||||
# add profile dir to our config
|
||||
self.config['local_profile_dir'] = self.profile.profile
|
||||
|
||||
# create results holder
|
||||
self.results_handler = RaptorResultsHandler()
|
||||
|
||||
|
@ -177,6 +166,13 @@ class Raptor(object):
|
|||
if test.get('type') == "benchmark":
|
||||
self.benchmark = Benchmark(self.config, test)
|
||||
benchmark_port = int(self.benchmark.port)
|
||||
|
||||
# for android we must make the benchmarks server available to the device
|
||||
if self.config['app'] == "geckoview" and self.config['host'] \
|
||||
in ('localhost', '127.0.0.1'):
|
||||
self.log.info("making the raptor benchmarks server port available to device")
|
||||
_tcp_port = "tcp:%s" % benchmark_port
|
||||
self.device.create_socket_connection('reverse', _tcp_port, _tcp_port)
|
||||
else:
|
||||
benchmark_port = 0
|
||||
|
||||
|
@ -188,12 +184,6 @@ class Raptor(object):
|
|||
b_port=benchmark_port,
|
||||
debug_mode=1 if self.debug_mode else 0)
|
||||
|
||||
# for android we must make the benchmarks server available to the device
|
||||
if self.config['app'] == "geckoview" and self.config['host'] in ('localhost', '127.0.0.1'):
|
||||
self.log.info("making the raptor benchmarks server port available to device")
|
||||
_tcp_port = "tcp:%s" % benchmark_port
|
||||
self.device.create_socket_connection('reverse', _tcp_port, _tcp_port)
|
||||
|
||||
# must intall raptor addon each time because we dynamically update some content
|
||||
# note: for chrome the addon is just a list of paths that ultimately are added
|
||||
# to the chromium command line '--load-extension' argument
|
||||
|
@ -213,11 +203,30 @@ class Raptor(object):
|
|||
if self.config['app'] in ["firefox", "geckoview"]:
|
||||
webext_id = self.profile.addons.addon_details(raptor_webext)['id']
|
||||
|
||||
# for android/geckoview, create a top-level raptor folder on the device
|
||||
# sdcard; if it already exists remove it so we start fresh each time
|
||||
if self.config['app'] == "geckoview":
|
||||
self.device_raptor_dir = "/sdcard/raptor"
|
||||
self.config['device_raptor_dir'] = self.device_raptor_dir
|
||||
if self.device.is_dir(self.device_raptor_dir):
|
||||
self.log.info("deleting existing device raptor dir: %s" % self.device_raptor_dir)
|
||||
self.device.rm(self.device_raptor_dir, recursive=True)
|
||||
self.log.info("creating raptor folder on sdcard: %s" % self.device_raptor_dir)
|
||||
self.device.mkdir(self.device_raptor_dir)
|
||||
self.device.chmod(self.device_raptor_dir, recursive=True)
|
||||
|
||||
# some tests require tools to playback the test pages
|
||||
if test.get('playback', None) is not None:
|
||||
self.get_playback_config(test)
|
||||
# startup the playback tool
|
||||
self.playback = get_playback(self.config)
|
||||
self.playback = get_playback(self.config, self.device)
|
||||
|
||||
# for android we must make the playback server available to the device
|
||||
if self.config['app'] == "geckoview" and self.config['host'] \
|
||||
in ('localhost', '127.0.0.1'):
|
||||
self.log.info("making the raptor playback server port available to device")
|
||||
_tcp_port = "tcp:8080"
|
||||
self.device.create_socket_connection('reverse', _tcp_port, _tcp_port)
|
||||
|
||||
if self.config['app'] in ("geckoview", "firefox") and \
|
||||
self.config['host'] not in ('localhost', '127.0.0.1'):
|
||||
|
@ -230,19 +239,36 @@ class Raptor(object):
|
|||
with open(userjspath, 'w') as userjsfile:
|
||||
userjsfile.writelines(prefs)
|
||||
|
||||
# for geckoview we must copy the profile onto the device and set perms
|
||||
# for geckoview/android pageload playback we can't use a policy to turn on the
|
||||
# proxy; we need to set prefs instead; note that the 'host' may be different
|
||||
# than '127.0.0.1' so we must set the prefs accordingly
|
||||
if self.config['app'] == "geckoview" and test.get('playback', None) is not None:
|
||||
self.log.info("setting profile prefs to turn on the geckoview browser proxy")
|
||||
no_proxies_on = "localhost, 127.0.0.1, %s" % self.config['host']
|
||||
proxy_prefs = {}
|
||||
proxy_prefs["network.proxy.type"] = 1
|
||||
proxy_prefs["network.proxy.http"] = self.config['host']
|
||||
proxy_prefs["network.proxy.http_port"] = 8080
|
||||
proxy_prefs["network.proxy.ssl"] = self.config['host']
|
||||
proxy_prefs["network.proxy.ssl_port"] = 8080
|
||||
proxy_prefs["network.proxy.no_proxies_on"] = no_proxies_on
|
||||
self.profile.set_preferences(proxy_prefs)
|
||||
|
||||
# now some final settings, and then startup of the browser under test
|
||||
if self.config['app'] == "geckoview":
|
||||
# for android/geckoview we must copy the profile onto the device and set perms
|
||||
if not self.device.is_app_installed(self.config['binary']):
|
||||
raise Exception('%s is not installed' % self.config['binary'])
|
||||
|
||||
self.log.info("copying firefox profile onto the android device")
|
||||
self.device_profile = "/sdcard/raptor-profile"
|
||||
self.device_profile = os.path.join(self.device_raptor_dir, "profile")
|
||||
if self.device.is_dir(self.device_profile):
|
||||
self.log.info("deleting existing device profile folder: %s" % self.device_profile)
|
||||
self.device.rm(self.device_profile, recursive=True)
|
||||
self.log.info("creating profile folder on device: %s" % self.device_profile)
|
||||
self.device.mkdir(self.device_profile)
|
||||
self.log.info("copying firefox profile onto the device")
|
||||
self.log.info("note: the profile folder being copied is: %s" % self.profile.profile)
|
||||
self.log.info('the adb push cmd copies that profile dir to a new temp dir before copy')
|
||||
self.device.push(self.profile.profile, self.device_profile)
|
||||
|
||||
self.log.info("setting permisions to profile dir on the device")
|
||||
self.device.chmod(self.device_profile, recursive=True)
|
||||
|
||||
# now start the geckoview app
|
||||
|
@ -425,7 +451,6 @@ class Raptor(object):
|
|||
self.device.remove_socket_connections('reverse')
|
||||
else:
|
||||
pass
|
||||
remove_autoconfig(self.config['binary'])
|
||||
self.log.info("finished")
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче