blob: 401afa2c7b88348e631d74e4812288620a24a421 [file] [log] [blame]
# Copyright 2020 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.
from contextlib import contextmanager
from recipe_engine import recipe_api
BUCKET_NAME = 'flutter_infra'
FUCHSIA_BUCKET_NAME = 'fuchsia'
FUCHSIA_SDK_CIPD = 'fuchsia/sdk/core/linux-amd64'
FUCHSIA_IMAGE_NAME = 'generic-x64.tgz'
FUCHSIA_PACKAGES_ARCHIVE_NAME = 'generic-x64.tar.gz'
FUCHSIA_TEST_SCRIPT_NAME = 'run_fuchsia_tests.sh'
SSH_CONFIG = """
Host *
CheckHostIP no
StrictHostKeyChecking no
ForwardAgent no
ForwardX11 no
GSSAPIDelegateCredentials no
UserKnownHostsFile /dev/null
User fuchsia
IdentitiesOnly yes
IdentityFile $FUCHSIA_PRIVATE_KEY
ControlPersist yes
ControlMaster auto
ControlPath /tmp/fuchsia--%r@%h:%p
ConnectTimeout 10
ServerAliveInterval 1
ServerAliveCountMax 10
LogLevel ERROR
"""
class FuchsiaUtilsApi(recipe_api.RecipeApi):
"""Provides utilities to execute fuchsia tests."""
@contextmanager
def make_temp_dir(self, label):
temp_dir = self.m.path.mkdtemp('tmp')
try:
yield temp_dir
finally:
self.m.file.rmtree('temp dir for %s' % label, temp_dir)
def get_fuchsia_version(self, flutter_bin):
"""Get the Fuchsia SDK version from the given Flutter SDK.
Args:
flutter_bin: Path to Flutter bin with internal/fuchsia-linux.version.
Returns:
String of the Fuchsia SDK version to pull artifacts from GCP.
"""
# Flutter SDK only stores the CIPD version, so CIPD must be queried to
# find the SDK version tag for this ref.
version_path = flutter_bin.join('internal', 'fuchsia-linux.version')
version = self.m.file.read_text('Read fuchsia cipd version', version_path)
fuchsia_cipd = self.m.cipd.describe(FUCHSIA_SDK_CIPD, version=version)
# There are multiple tags in a Fuchsia SDK CIPD description requiring
# a search through the tags tuple for the version tag.
for tag in fuchsia_cipd.tags:
if 'version:' in tag.tag:
return tag.tag.replace('version:', '')
raise recipe_api.InfraFailure('No version tag on Fuchsia SDK CIPD ref')
def download_fuchsia_deps(self, flutter_bin, destination_path):
"""Download dependencies to initialize Fuchsia bot.
Args:
flutter_bin: Path to Flutter bin with internal/fuchsia-linux.version.
destination_path: Path to store the downloaded Fuchsia dependencies.
"""
with self.m.step.nest('Download Fuchsia Dependencies'):
fuchsia_version = self.get_fuchsia_version(flutter_bin)
self.m.gsutil.download(
FUCHSIA_BUCKET_NAME,
'development/%s/images/%s' % (fuchsia_version, FUCHSIA_IMAGE_NAME),
destination_path,
name="download fuchsia system image")
self.m.gsutil.download(
FUCHSIA_BUCKET_NAME,
'development/%s/packages/%s' %
(fuchsia_version, FUCHSIA_PACKAGES_ARCHIVE_NAME),
destination_path,
name="download fuchsia companion packages")
def copy_tool_deps(self, checkout_path, destination_path):
"""Copy necessary tools from Flutter SDK to initialize Fuchsia bot.
Args:
flutter_bin: Path to Flutter bin with internal/fuchsia-linux.version.
destination_path: Path to store the downloaded Fuchsia dependencies.
"""
flutter_bin = checkout_path.join('bin')
fuchsia_tools = flutter_bin.join('cache', 'artifacts', 'fuchsia', 'tools', 'x64')
self.download_fuchsia_deps(flutter_bin, destination_path)
with self.m.step.nest('Collect tool deps'):
self.m.file.copy(
'Copy test script',
checkout_path.join('dev', 'bots', FUCHSIA_TEST_SCRIPT_NAME),
destination_path)
self.m.file.copy('Copy device-finder',
fuchsia_tools.join('device-finder'), destination_path)
self.m.file.copy('Copy pm', fuchsia_tools.join('pm'), destination_path)
def collect_results(self, fuchsia_swarming_metadata, timeout='30m'):
# Collect the result of the task by metadata.
fuchsia_output = self.m.path['cleanup'].join('fuchsia_test_output')
self.m.file.ensure_directory('swarming output', fuchsia_output)
results = self.m.swarming.collect(
'collect',
fuchsia_swarming_metadata,
output_dir=fuchsia_output,
timeout=timeout)
self.m.display_util.display_tasks(
'Display builds',
results=results,
metadata=fuchsia_swarming_metadata,
raise_on_failure=True)
def upload_deps(self, checkout_path):
with self.m.step.nest('Create CAS Archive'):
with self.make_temp_dir('cas_dir') as cas_dir:
self.copy_tool_deps(checkout_path, cas_dir)
cas_flutter = cas_dir.join('flutter')
self.m.file.copytree('Copy flutter framework', checkout_path,
cas_flutter)
return self.m.cas_util.upload(cas_dir, step_name='Archive Fuchsia Test CAS')
def trigger_swarming_task(self, checkout_path):
cas_hash = self.upload_deps(checkout_path)
fuchsia_ctl_package = self.m.cipd.EnsureFile()
fuchsia_ctl_package.add_package(
'flutter/fuchsia_ctl/${platform}',
self.m.properties.get('fuchsia_ctl_version'))
request = (
self.m.swarming.task_request().with_name(
'flutter_fuchsia_driver_tests').with_priority(100))
request = (
request.with_slice(
0,
request[0].with_cipd_ensure_file(fuchsia_ctl_package).with_command([
'./%s' % FUCHSIA_TEST_SCRIPT_NAME, FUCHSIA_IMAGE_NAME
]).with_dimensions(pool='luci.flutter.tests').with_cas_input_root(
cas_hash).with_expiration_secs(3600).with_io_timeout_secs(
3600).with_execution_timeout_secs(3600).with_idempotent(
True).with_containment_type('AUTO')))
return self.m.swarming.trigger(
'Trigger Fuchsia Driver Tests', requests=[request])
def run_test(self, checkout_path):
"""Create a swarming task to run tests against Fuchsia device.
Args:
checkout_path: Location of Flutter SDK
"""
with self.m.step.nest('Fuchsia Tests'):
self.m.step(
'Flutter Config Enable Fuchsia',
['flutter', 'config', '--enable-fuchsia'])
self.m.step(
'Precache Flutter Artifacts',
['flutter', 'precache', '--fuchsia', '--no-android', '--no-ios', '--force'])
self.m.step('Precache Flutter Runners', [
'flutter', 'precache', '--flutter_runner', '--no-android', '--no-ios'
])
return self.trigger_swarming_task(checkout_path)
def device_name(self):
"""Extracts the device name from the bot name.
This function expects a bot name [host]--[device_name] where host
is the hostname the bot is running on and [device_name] is the
fuchsia device name, e.g. fuchsia-tests-lab01-0001--ocean-bats-wick-snub.
"""
return self.m.swarming.bot_id.split('--')[1]
def fuchsia_environment(self, checkout_path):
env, env_paths = self.m.repo_util.flutter_environment(checkout_path)
private_key_path = '/etc/botanist/keys/id_rsa_infra'
config_path = self.m.path['cleanup'].join('fuchsia_ssh__config')
public_key_path = self.m.path['cleanup'].join('fuchsia_key.pub')
with self.m.step.nest('Prepare Environment'):
self.m.step(
'Create public key', ['ssh-keygen', '-y', '-f', private_key_path],
stdout=self.m.raw_io.output_text(leak_to=public_key_path))
self.m.file.write_raw('Create ssh_config', config_path, SSH_CONFIG)
env['FUCHSIA_SSH_CONFIG'] = config_path
env['FUCHSIA_PRIVATE_KEY'] = private_key_path
env['FUCHSIA_PUBLIC_KEY'] = public_key_path
return env, env_paths