blob: 23b3a3fe65793b9045f9e6e74bb8d4aa0ca94614 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {argh::FromArgs, ffx_core::ffx_command};
/// Manage updates: query/set update channel, kick off a check for update, force
/// an update (to any point, i.e. a downgrade can be requested).
#[ffx_command()]
#[derive(FromArgs, Debug, PartialEq)]
#[argh(
subcommand,
name = "update",
description = "Update base system software on target",
note = "This command interfaces with system update services on the target."
)]
pub struct Update {
#[argh(subcommand)]
pub cmd: Command,
}
/// Subcommands for `update`.
#[derive(Debug, Eq, FromArgs, PartialEq)]
#[argh(subcommand)]
pub enum Command {
// fuchsia.update.channelcontrol.ChannelControl protocol:
Channel(Channel),
// fuchsia.update Manager protocol:
CheckNow(CheckNow),
// fuchsia.update.installer protocol:
ForceInstall(ForceInstall),
}
/// Get the current (running) channel.
#[derive(Debug, Eq, FromArgs, PartialEq)]
#[argh(
subcommand,
name = "channel",
description = "View and manage update channels",
note = "Channel management commands and operations. Interfaces directly with
the 'fuchsia.update.channelcontrol.ChannelControl' service on the target
system."
)]
pub struct Channel {
#[argh(subcommand)]
pub cmd: channel::Command,
}
/// Subcommands for `channel`.
// TODO(fxbug.dev/60016): Make get/set symmetrical.
pub mod channel {
use argh::FromArgs;
#[derive(Debug, Eq, FromArgs, PartialEq)]
#[argh(subcommand)]
pub enum Command {
Get(Get),
Target(Target),
Set(Set),
List(List),
}
/// Get the current channel
#[derive(Debug, Eq, FromArgs, PartialEq)]
#[argh(
subcommand,
name = "get-current",
description = "Return the currently configured update channel",
note = "For developer product configurations, this is by default 'devhost'.",
error_code(1, "Timeout while getting update channel.")
)]
pub struct Get {}
/// Get the target channel
#[derive(Debug, Eq, FromArgs, PartialEq)]
#[argh(
subcommand,
name = "get-next",
description = "Return the next or target update channel",
note = "Returns the next or target channel. This differs from `get` when the
next successful update changes the configured update channel on the
system.",
error_code(1, "Timeout while getting update channel.")
)]
pub struct Target {}
/// Set the target channel
#[derive(Debug, Eq, FromArgs, PartialEq)]
#[argh(
subcommand,
name = "set",
example = "To list all the known update channels:
$ ffx target update channel list
Then, use a valid channel from the list:
$ ffx target update channel set <channel>",
description = "Sets the update channel",
note = "Sets the next or target update channel on the device. When paired with
`ffx target update check-now`, ensures the update is check against the
next or target channel. When the update is successful, next or target
channel becomes the current channel.
Use `ffx target update channel list` to list known system update
channels.",
error_code(1, "Timeout while setting update channel.")
)]
pub struct Set {
#[argh(positional)]
pub channel: String,
}
/// List the known target channels
#[derive(Debug, Eq, FromArgs, PartialEq)]
#[argh(
subcommand,
name = "list",
description = "List the known update channels",
note = "This lists all the known next or target update channels on the system.
Returns an empty list if no other update channels are configured.",
error_code(1, "Timeout while getting list of update channel.")
)]
pub struct List {}
}
/// Start an update. If no newer update is available, no update is performed.
#[derive(Debug, Eq, FromArgs, PartialEq)]
#[argh(
subcommand,
name = "check-now",
example = "To check for update and monitor progress:
$ ffx target update check-now --monitor",
description = "Check and perform the system update operation",
note = "Triggers an update check operation and performs the update if available.
Interfaces using the 'fuchsia.update Manager' protocol with the system
update service on the target.
The command takes in an optional `--monitor` switch to watch the progress
of the update. The output is displayed in `stdout`.
The command also takes an optional `--service-initiated` switch to indicate
a separate service has initiated a check for update."
)]
pub struct CheckNow {
/// the update check was initiated by a service, in the background.
#[argh(switch)]
pub service_initiated: bool,
/// monitor for state update.
#[argh(switch)]
pub monitor: bool,
}
/// Directly invoke the system updater to install the provided update, bypassing
/// any update checks.
#[derive(Debug, Eq, FromArgs, PartialEq)]
#[argh(
subcommand,
name = "force-install",
example = "With a known update package URL, trigger an update:
$ ffx target update force-install fuchsia-pkg://fuchsia.com/update
Also trigger a reboot after update:
$ ffx target update force-install
fuchsia-pkg://fuchsia.com/update
--reboot",
description = "Trigger the system updater manually",
note = "Directly invoke the system updater to install the provided update,
bypassing any update checks.
Interfaces using the 'fuchsia.update.installer' protocol to update the
system. Requires an <update_pkg_url> in the following format:
`fuchsia-pkg://fuchsia.com/update`
Takes an optional `--reboot <true|false>` to trigger a system reboot
after update has been successfully applied."
)]
pub struct ForceInstall {
/// automatically trigger a reboot into the new system
#[argh(option, default = "true")]
pub reboot: bool,
/// the url of the update package describing the update to install
#[argh(positional)]
pub update_pkg_url: String,
}
#[cfg(test)]
mod tests {
use super::*;
use matches::assert_matches;
#[test]
fn test_unknown_option() {
assert_matches!(Update::from_args(&["update"], &["--unknown"]), Err(_));
}
#[test]
fn test_unknown_subcommand() {
assert_matches!(Update::from_args(&["update"], &["unknown"]), Err(_));
}
#[test]
fn test_channel_get() {
let update = Update::from_args(&["update"], &["channel", "get-current"]).unwrap();
assert_eq!(
update,
Update {
cmd: Command::Channel(Channel { cmd: channel::Command::Get(channel::Get {}) })
}
);
}
#[test]
fn test_channel_target() {
let update = Update::from_args(&["update"], &["channel", "get-next"]).unwrap();
assert_eq!(
update,
Update {
cmd: Command::Channel(Channel {
cmd: channel::Command::Target(channel::Target {})
})
}
);
}
#[test]
fn test_channel_set() {
let update = Update::from_args(&["update"], &["channel", "set", "new-channel"]).unwrap();
assert_eq!(
update,
Update {
cmd: Command::Channel(Channel {
cmd: channel::Command::Set(channel::Set { channel: "new-channel".to_string() })
})
}
);
}
#[test]
fn test_channel_list() {
let update = Update::from_args(&["update"], &["channel", "list"]).unwrap();
assert_eq!(
update,
Update {
cmd: Command::Channel(Channel { cmd: channel::Command::List(channel::List {}) })
}
);
}
#[test]
fn test_check_now() {
let update = Update::from_args(&["update"], &["check-now"]).unwrap();
assert_eq!(
update,
Update {
cmd: Command::CheckNow(CheckNow { service_initiated: false, monitor: false })
}
);
}
#[test]
fn test_check_now_monitor() {
let update = Update::from_args(&["update"], &["check-now", "--monitor"]).unwrap();
assert_eq!(
update,
Update { cmd: Command::CheckNow(CheckNow { service_initiated: false, monitor: true }) }
);
}
#[test]
fn test_check_now_service_initiated() {
let update = Update::from_args(&["update"], &["check-now", "--service-initiated"]).unwrap();
assert_eq!(
update,
Update { cmd: Command::CheckNow(CheckNow { service_initiated: true, monitor: false }) }
);
}
#[test]
fn test_force_install_requires_positional_arg() {
assert_matches!(Update::from_args(&["update"], &["force-install"]), Err(_));
}
#[test]
fn test_force_install() {
let update = Update::from_args(&["update"], &["force-install", "url"]).unwrap();
assert_eq!(
update,
Update {
cmd: Command::ForceInstall(ForceInstall {
update_pkg_url: "url".to_owned(),
reboot: true,
})
}
);
}
#[test]
fn test_force_install_no_reboot() {
let update =
Update::from_args(&["update"], &["force-install", "--reboot", "false", "url"]).unwrap();
assert_eq!(
update,
Update {
cmd: Command::ForceInstall(ForceInstall {
update_pkg_url: "url".to_owned(),
reboot: false,
})
}
);
}
}