Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/fuchsia_ctl/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
# CHANGELOG

## 0.0.22

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hard to control because when releasing we usually found errors and we need to create new versions skipping some numbers. The current prod version is 0.22

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, okay. Fixed.


- Added `emu` tool for spawning an emulator instance given a Fuchsia QEMU build,
the Fuchsia SDK, and an Android emulator executable.
- Fixed homepage link.

## 0.0.18 - 0.0.21

- Add retries to paving operations.
- Add timeouts to paving and ssh operations.
- Add arguments parameter to test command.



## 0.0.17

- Add "push-packages" option.
Expand Down
41 changes: 37 additions & 4 deletions packages/fuchsia_ctl/bin/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,18 @@ import 'dart:io';
import 'package:args/args.dart';
import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:fuchsia_ctl/fuchsia_ctl.dart';
import 'package:fuchsia_ctl/src/amber_ctl.dart';
import 'package:fuchsia_ctl/src/operation_result.dart';
import 'package:fuchsia_ctl/src/tar.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
import 'package:retry/retry.dart';
import 'package:uuid/uuid.dart';

import 'package:fuchsia_ctl/fuchsia_ctl.dart';

typedef AsyncResult = Future<OperationResult> Function(
String, DevFinder, ArgResults);

const Map<String, AsyncResult> commands = <String, AsyncResult>{
'emu': emulator,
'pave': pave,
'pm': pm,
'ssh': ssh,
Expand All @@ -42,6 +41,18 @@ Future<void> main(List<String> args) async {
defaultsTo: './dev_finder',
help: 'The path to the dev_finder executable.')
..addFlag('help', defaultsTo: false, help: 'Prints help.');

parser.addCommand('emu')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a blocking command? e.g it will remain running until calling ctrl+c?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it's blocking. Should that be documented?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in the code. I couldn't find a way to configure it to show with ArgParser

..addOption('image', help: 'Fuchsia image to run')
..addOption('zbi', help: 'Bootloader image to sign and run')
..addOption('qemu-kernel', help: 'QEMU kernel to run')
..addOption('window-size', help: 'Emulator window size formatted "WxH"')
..addOption('aemu', help: 'AEMU executable path')
..addOption('sdk',
help: 'Location to Fuchsia SDK containing tools and images')
..addOption('ssh-path', defaultsTo: '.fuchsia', help: 'Path to ssh keys')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is different from the defaults in the other commands ./.ssh

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally all the other commands are only passing the private key. I'd recommend to implement the same behavior here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh I see. Switched over to that. Is it okay to leave the internal reference of it in Emulator as authorizedKeys, or should that also be switched to be called public key?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it will be a good idea to try to use the same names across the utility.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added. Switched to using the SshKeyManager for handling authorized keys.

..addFlag('headless', help: 'Run FEMU without graphical window');

parser.addCommand('ssh')
..addFlag('interactive',
abbr: 'i',
Expand Down Expand Up @@ -129,6 +140,28 @@ Future<void> main(List<String> args) async {
}
}

@visibleForTesting
Future<OperationResult> emulator(
String deviceName,
DevFinder devFinder,
ArgResults args,
) async {
final Emulator emulator = Emulator(
aemuPath: args['aemu'],
fuchsiaImagePath: args['image'],
fuchsiaSdkPath: args['sdk'],
qemuKernelPath: args['qemu-kernel'],
sshPath: args['ssh-path'],
zbiPath: args['zbi'],
);
await emulator.prepareEnvironment();

return emulator.start(
headless: args['headless'],
windowSize: args['window-size'],
);
}

@visibleForTesting
Future<OperationResult> ssh(
String deviceName,
Expand Down
3 changes: 3 additions & 0 deletions packages/fuchsia_ctl/lib/fuchsia_ctl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

export 'src/amber_ctl.dart';
export 'src/dev_finder.dart';
export 'src/emulator.dart';
export 'src/image_paver.dart';
export 'src/operation_result.dart';
export 'src/package_server.dart';
export 'src/ssh_client.dart';
export 'src/tar.dart';
4 changes: 1 addition & 3 deletions packages/fuchsia_ctl/lib/src/amber_ctl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
import 'dart:io';

import 'package:meta/meta.dart';
import 'package:fuchsia_ctl/fuchsia_ctl.dart';
import 'package:fuchsia_ctl/src/ssh_client.dart';
import 'package:uuid/uuid.dart';

import 'operation_result.dart';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't needed anymore?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was clean up as it was already imported from the high level fuchsia_ctl since this imports the ssh class.

import 'package:fuchsia_ctl/fuchsia_ctl.dart';

const SshClient _kSsh = SshClient();

Expand Down
232 changes: 232 additions & 0 deletions packages/fuchsia_ctl/lib/src/emulator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
// Copyright 2019 The Flutter Authors. All rights reserved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2020?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This work was derivative of the package server file, so it's left as 2019.

// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:io';

import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
import 'package:process/process.dart';

import 'operation_result.dart';

/// A wrapper for running Fuchsia images on the Android Emulator (AEMU).
class Emulator {
/// Creates a new wrapper for the `emu` tool.
Emulator({
@required this.aemuPath,
@required this.fuchsiaImagePath,
@required this.fuchsiaSdkPath,
this.fs = const LocalFileSystem(),
this.processManager = const LocalProcessManager(),
@required this.qemuKernelPath,
this.sshPath = '.fuchsia',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use private key instead.

@required this.zbiPath,
}) : assert(processManager != null);

/// The path to the AEMU executable on disk.
final String aemuPath;

/// Fuchsia image to load into the emulator.
final String fuchsiaImagePath;

/// The path to the Fuchsia SDK that contains the tools `fvm` and `zbi`.
final String fuchsiaSdkPath;

/// The QEMU kernel image to use. This is only bundled in Fuchsia QEMU images.
final String qemuKernelPath;

/// The path to the directory containing authorized_keys.
final String sshPath;

/// The Fuchsia bootloader image.
final String zbiPath;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to pass this path if it is already in the sdk? or the comment for fuchsiaSdkPath needs to get updated?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I flipped through several iterations of naming this. This is the zircon-a.zbi file included in a release image. There is also a tool called zbi for working with these in the sdk. I originally had zirconBootImagePath. Do you think it would be helpful to use that instead when referring to the image instead of the tool?


/// Location of `fvm` in [fuchsiaSdkPath].
@visibleForTesting
final String fvmToolPath = 'sdk/tools/fvm';

/// Location of `zbi` in [fuchsiaSdkPath].
@visibleForTesting
final String zbiToolPath = 'sdk/tools/zbi';

/// Default AEMU window size to be launched.
@visibleForTesting
final String defaultWindowSize = '1280x800';

/// Flag to pass to AEMU to run in headless mode.
@visibleForTesting
final String aemuHeadlessFlag = '-no-window';

/// The [FileSystem] to use when running the `emu` tool.
final FileSystem fs;

/// The [ProcessManager] to use for running the `emu` tool.
final ProcessManager processManager;

/// FVM extended image of [fuchsiaImagePath] for running on FEMU.
@visibleForTesting
String fvmImagePath;

/// [zbiPath] that is accessible with SSH using [sshPath] keys.
@visibleForTesting
String signedZbiPath;

/// Update given Fuchsia assets to make them compatible with FEMU.
///
/// 1. Ensure required assets exist.
/// 2. Create FVM image for running with FEMU.
/// 3. Sign boot image for host access to the guest FEMU instance.
Future<void> prepareEnvironment() async {
assert(fs.isFileSync(fuchsiaImagePath));
assert(fs.isFileSync(zbiPath));
assert(fs.isFileSync(qemuKernelPath));

final String tmpPath = fs.systemTempDirectory.createTempSync().path;
fvmImagePath = '$tmpPath/fvm.blk';
signedZbiPath = '$tmpPath/fuchsia-ssh.zbi';

await _prepareFvmImage(fuchsiaImagePath, fvmImagePath);
await _signBootImage(zbiPath, signedZbiPath);
}

/// Double the size of [fuchsiaImagePath] to make space for the emulator
/// to write back to it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for when applications or tests are run on the emulator, the emulator has space to write back to to add those files. This could be extended to take a precise value to mock devices better, but this is the current logic fx emu does.

Future<void> _prepareFvmImage(String fuchsiaImagePath, String fvmPath,
{String fvmExecutable}) async {
fvmExecutable ??= path.join(fuchsiaSdkPath, fvmToolPath);

await _run(<String>['cp', fuchsiaImagePath, fvmPath]);

/// [fvmTool] and FEMU need write access to [fvmPath].
await _run(<String>['chmod', 'u+w', fvmPath]);

// Calculate new size by doubling the current size
final File fvmFile = fs.file(fvmPath)..createSync();
final int newSize = fvmFile.lengthSync() * 2;

await _run(
<String>[fvmExecutable, fvmPath, 'extend', '--length', '$newSize']);
}

/// Signed [zbiPath] using [zbiExecutable] with [authorizedKeysPath] to
/// create a bootloader image that is accessible from the host.
Future<void> _signBootImage(String zbiPath, String signedZbiPath,
{String zbiExecutable, String authorizedKeysPath}) async {
zbiExecutable ??= path.join(fuchsiaSdkPath, zbiToolPath);
authorizedKeysPath ??= path.join(sshPath, 'authorized_keys');

/// Ensure `zbi` is able to find the ssh keys by giving the full path.
final File authorizedKeysAbsolute = fs.file(authorizedKeysPath).absolute;

final List<String> zbiCommand = <String>[
zbiExecutable,
'--compressed=zstd',
'-o',
signedZbiPath,
zbiPath,
'-e',
'data/ssh/authorized_keys=${authorizedKeysAbsolute.path}'
];
await _run(zbiCommand);
}

/// Launch AEMU with [fvmImagePath], [signedZbiPath], and [qemuKernelPath].
///
/// [prepareEnvironment] must have been called before starting the emulator.
///
/// If [headless] is true, AEMU will run without a graphical window. Infra
/// will run AEMU in headless mode.
///
/// [windowSize] is what AEMU will set its window size to. Defaults to
/// [defaultWindowSize]. Expected to be in the format of "WIDTHxHEIGHT".
Future<OperationResult> start(
{bool headless = false, String windowSize}) async {
assert(fvmImagePath != null && fs.isFileSync(fvmImagePath));
assert(signedZbiPath != null && fs.isFileSync(signedZbiPath));

final List<String> aemuCommand = <String>[
aemuPath,
'-feature',
'VirtioInput,RefCountPipe,KVM,GLDirectMem,Vulkan',
'-window-size',
windowSize ?? defaultWindowSize,
'-gpu',
'swiftshader_indirect',
if (headless) aemuHeadlessFlag,
];

/// Anything after -fuchsia flag will be passed to QEMU
aemuCommand.addAll(<String>[
'-fuchsia',
'-kernel', qemuKernelPath,
'-initrd', signedZbiPath,
'-m', '2048',
'-serial', 'stdio',
'-vga', 'none',
'-device', 'virtio-keyboard-pci',
'-device', 'virtio_input_multi_touch_pci_1',
'-smp', '4,threads=2',
'-machine', 'q35',
'-device', 'isa-debug-exit,iobase=0xf4,iosize=0x04',
// TODO(chillers): Add hardware acceleration option to configure this.
'-enable-kvm',
'-cpu', 'host,migratable=no,+invtsc',
'-netdev', 'type=tap,ifname=qemu,script=no,downscript=no,id=net0',
'-device', 'e1000,netdev=net0,mac=52:54:00:63:5e:7a',
'-drive', 'file=$fvmImagePath,format=raw,if=none,id=vdisk',
'-device', 'virtio-blk-pci,drive=vdisk',
'-append',
// TODO(chillers): Generate entropy mixin.
'\'TERM=xterm-256color kernel.serial=legacy kernel.entropy-mixin=660486b6b20b4ace3fb5c81b0002abf5271289185c6a5620707606c55b377562 kernel.halt-on-panic=true\'',
]);

await _start(aemuCommand);

return OperationResult.success();
}

/// Helper function for running [command] and manging its stdio and errors.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be abstracted to a utilities module?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added. I moved this to CommandLine helper class, but i'm not set on the name.

Future<void> _run(List<String> command) async {
stdout.writeln(command.join(' '));
final ProcessResult process = await processManager.run(
command,
);
stdout.writeln(process.stdout);
stderr.writeln(process.stderr);

if (process.exitCode != 0) {
throw EmulatorException('${command.first} did not return exit code 0');
}
}

/// Helper function for starting [command] and piping its stdio and errors.
Future<Process> _start(List<String> command) async {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, looks like a generic function that can be reused in some other places.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved

stdout.writeln(command.join(' '));
final Process process = await processManager.start(
command,
);
stdout.addStream(process.stdout);
stderr.addStream(process.stderr);

if (await process.exitCode != 0) {
throw EmulatorException('${command.first} did not return exit code 0');
}

return process;
}
}

/// Wraps exceptions thrown by [Emulator].
class EmulatorException implements Exception {
/// Creates a new [EmulatorException].
const EmulatorException(this.message);

/// The user-facing message to display.
final String message;

@override
String toString() => message;
}
3 changes: 2 additions & 1 deletion packages/fuchsia_ctl/lib/src/package_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import 'dart:io';

import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:fuchsia_ctl/fuchsia_ctl.dart';
import 'package:process/process.dart';
import 'package:path/path.dart' as path;
import 'package:uuid/uuid.dart';

import 'package:fuchsia_ctl/fuchsia_ctl.dart';

/// A wrapper around the Fuchsia SDK `pm` tool.
class PackageServer {
/// Creates a new package server.
Expand Down
7 changes: 7 additions & 0 deletions packages/fuchsia_ctl/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.4"
retry:
dependency: "direct main"
description:
name: retry
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0+1"
shelf:
dependency: transitive
description:
Expand Down
4 changes: 2 additions & 2 deletions packages/fuchsia_ctl/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ description: >
A Dart package for paving, serving packages to, and running commands on a
Fuchsia Device. This package is primarily intended for use in Flutter's
continuous integration/testing infrastructure.
homepage: https://github.com/flutter/packags/tree/master/packages/fuchsia_ctl
version: 0.0.21
homepage: https://github.com/flutter/packages/tree/master/packages/fuchsia_ctl
version: 0.0.22

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

23?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


environment:
sdk: ">=2.4.0 <3.0.0"
Expand Down
Loading