
Etcher currently elevates a child writer proxy that itself spawns the Etcher CLI in robot mode, parses the output, and proxies those messages to the GUI application over IPC. After these set of changes, Etcher elevates a single child writer process that directly communicates back with the GUI using IPC. The main purpose behind these changes is to simplify the overall architecture and fix various issues caused by the current complex child process tree. Here's a summary of the changes: - Stop wrapping the Etcher CLI to perform writing - Remove the robot option from the Etcher CLI (along with related documentation) - Elevate a new `child-write.js` standalone executable - Move the relevant bits of `lib/child-writer` to the `image-writer` GUI module - Remove the `lib/child-writer` directory - Add a new "Child died unexpectedly" Mixpanel event - Floor state percentage in the flash state model The above changes made is possible to tackle all the remaining issues where the writer process would remain alive even if the parent died. Change-Type: patch Changelog-Entry: Ensure the writer process dies when the GUI application is killed. See: https://github.com/resin-io/etcher/pull/1873 See: https://github.com/resin-io/etcher/pull/1843 Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
The "robot" mechanism
The "robot" module is an entity that implements a text-based protocol to share objects between processes.
The contents and structure of these messages is what the "robot" module is mainly concerned with. Each "message" consists of a type (a "command" in robot parlance) and an arbitrary data object:
String command
: the message command nameObject data
: the message data
For example:
Child process:
robot.printMessage('my-message-type', {
my: {
message: 'data'
}
});
Parent process:
const message = robot.parseMessage(line);
console.log(robot.getCommand(message));
> 'my-message-type'
console.log(robot.getData(message));
> {
> my: {
> message: 'data'
> }
> }
Logging debug data to the console:
Child process:
// This will log the passed data to parent's console,
// as `console.log()`ing in the child will cause errors
robot.log({ debugging: 'things' })
The codename "robot" is inspired by xz, which provides a --robot
option that makes the tool print machine-parseable output:
--robot
Print messages in a machine-parsable format. This is intended
to ease writing frontends that want to use xz instead of
liblzma, which may be the case with various scripts. The output
with this option enabled is meant to be stable across xz
releases. See the section ROBOT MODE for details.
To enable the "robot" option, we standardised the presence of an
ETCHER_CLI_ROBOT
environment variable. You can check if the mode is enabled
by using the .isEnabled()
static function that the robot module provides:
if (robot.isEnabled()) {
console.log('The robot option is enabled');
}
The current protocol that we use is based on JSON. The writer process stringifies a JSON object, and prints it. The client then gets the line, parses it as JSON, and accesses the object.
For example, the writer process may have a fictitious internal object that looks like this:
{
percentage: 50,
stage: 'validation'
}
That object can be stringified as {"percentage":50,"stage":"validation"}
and
printed to stdout
.
This is what a valid robot message looks like:
{
"command": "progress",
"data": {
"percentage": 50
}
}
The command content and the data associated with it are application specific, however the robot module defines a single command called "error", which is used to transmit a JavaScript Error object, along with its metadata (stacktrace, code, description, etc) as a string.
You don't have to worry about the internal details of how an Error object is
encoded/decoded, given that the robot module exposes two high level utility
functions: .printError()
and .recomposeErrorMessage()
.
Here's an example of these functions in action:
const error = errors.createError({
title: 'This is an error',
description: 'My description'
});
robot.printError(error);
The client can then fetch the line, and recompose it back:
const error = robot.recomposeErrorMessage(line);
The resulting error
inherits the stacktrace and other metadata from the
original error, even if this was created in another process. This is how the
writer process propagates informational errors to the GUI.