Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: provide faucet upgrade command #1452

Merged
merged 1 commit into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
84 changes: 72 additions & 12 deletions sn_node_manager/src/bin/cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,16 @@ pub enum SubCmd {
#[clap(long)]
port: Option<u16>,
#[clap(long)]
/// Specify an Ipv4Addr for the node's RPC service to run on. This is useful if you want to expose the
/// RPC server outside. The ports are assigned automatically.
/// Specify an Ipv4Addr for the node's RPC server to run on.
///
/// Useful if you want to expose the RPC server pubilcly. Ports are assigned automatically.
///
/// If not set, the RPC server is run locally.
rpc_address: Option<Ipv4Addr>,
/// Provide environment variables for the safenode service.
///
/// This is useful to set the safenode's log levels. Each variable should be comma separated without any space.
/// Useful to set safenode's log levels. Variables should be comma separated without
/// spaces.
///
/// Example: --env SN_LOG=all,RUST_LOG=libp2p=debug
#[clap(name = "env", long, use_value_delimiter = true, value_parser = parse_environment_variables)]
Expand Down Expand Up @@ -125,7 +127,7 @@ pub enum SubCmd {
Join {
/// Set to build the safenode and faucet binaries.
///
/// This assumes the command is being run from the root of the safe_network repository.
/// This option requires the command run from the root of the safe_network repository.
#[clap(long)]
build: bool,
/// The number of nodes to run.
Expand Down Expand Up @@ -190,7 +192,7 @@ pub enum SubCmd {
Run {
/// Set to build the safenode and faucet binaries.
///
/// This assumes the command is being run from the root of the safe_network repository.
/// This option requires the command run from the root of the safe_network repository.
#[clap(long)]
build: bool,
/// Set to remove the client data directory and kill any existing local network.
Expand All @@ -199,7 +201,7 @@ pub enum SubCmd {
/// The number of nodes to run.
#[clap(long, default_value_t = DEFAULT_NODE_COUNT)]
count: u16,
/// Path to a faucet binary
/// Path to a faucet binary.
///
/// The path and version arguments are mutually exclusive.
#[clap(long, conflicts_with = "faucet_version", conflicts_with = "build")]
Expand Down Expand Up @@ -287,8 +289,7 @@ pub enum SubCmd {
/// Set this flag to force the upgrade command to replace binaries without comparing any
/// version numbers.
///
/// This may be required in a case where we want to 'downgrade' in case an upgrade caused a
/// problem, or for testing purposes.
/// Required if we want to downgrade, or for testing purposes.
#[clap(long)]
force: bool,
/// The peer ID of the service to upgrade
Expand All @@ -297,15 +298,17 @@ pub enum SubCmd {
/// The name of the service to upgrade
#[clap(long, conflicts_with = "peer_id")]
service_name: Option<String>,
/// Provide environment variables for the safenode service. This will override the values set during the Add
/// command.
/// Provide environment variables for the safenode service.
///
/// This is useful to set the safenode's log levels. Each variable should be comma separated without any space.
/// Values set when the service was added will be overridden.
///
/// Useful to set safenode's log levels. Variables should be comma separated without
/// spaces.
///
/// Example: --env SN_LOG=all,RUST_LOG=libp2p=debug
#[clap(name = "env", long, use_value_delimiter = true, value_parser = parse_environment_variables)]
env_variables: Option<Vec<(String, String)>>,
/// Provide a binary to upgrade to, using a URL.
/// Provide a binary to upgrade to using a URL.
///
/// The binary must be inside a zip or gzipped tar archive.
///
Expand Down Expand Up @@ -401,6 +404,46 @@ pub enum FaucetSubCmd {
/// This command must run as the root/administrative user.
#[clap(name = "stop")]
Stop {},
/// Upgrade the faucet.
///
/// The running faucet will be stopped, its binary will be replaced, then it will be started
/// again.
///
/// This command must run as the root/administrative user.
#[clap(name = "upgrade")]
Upgrade {
/// Set this flag to upgrade the faucet without starting it.
///
/// Can be useful for testing scenarios.
#[clap(long)]
do_not_start: bool,
/// Set this flag to force the upgrade command to replace binaries without comparing any
/// version numbers.
///
/// Required if we want to downgrade, or for testing purposes.
#[clap(long)]
force: bool,
/// Provide environment variables for the faucet service.
///
/// Values set when the service was added will be overridden.
///
/// Useful to set safenode's log levels. Variables should be comma separated without
/// spaces.
///
/// Example: --env SN_LOG=all,RUST_LOG=libp2p=debug
#[clap(name = "env", long, use_value_delimiter = true, value_parser = parse_environment_variables)]
env_variables: Option<Vec<(String, String)>>,
/// Provide a binary to upgrade to using a URL.
///
/// The binary must be inside a zip or gzipped tar archive.
///
/// This can be useful for testing scenarios.
#[clap(long, conflicts_with = "version")]
url: Option<String>,
/// Upgrade to a specific version rather than the latest version.
#[clap(long)]
version: Option<String>,
},
}

#[tokio::main(flavor = "current_thread")]
Expand Down Expand Up @@ -458,6 +501,23 @@ async fn main() -> Result<()> {
}
FaucetSubCmd::Start {} => cmd::faucet::start(verbosity).await,
FaucetSubCmd::Stop {} => cmd::faucet::stop(verbosity).await,
FaucetSubCmd::Upgrade {
do_not_start,
force,
env_variables: provided_env_variable,
url,
version,
} => {
cmd::faucet::upgrade(
do_not_start,
force,
provided_env_variable,
url,
version,
verbosity,
)
.await
}
},
SubCmd::Join {
build,
Expand Down
73 changes: 71 additions & 2 deletions sn_node_manager/src/cmd/faucet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@
// KIND, either express or implied. Please review the Licences for the specific language governing
// permissions and limitations relating to use of the SAFE Network Software.

use super::is_running_as_root;
use super::{download_and_get_upgrade_bin_path, is_running_as_root, print_upgrade_summary};
use crate::{
add_services::{add_faucet, config::AddFaucetServiceOptions},
config,
helpers::download_and_extract_release,
ServiceManager, VerbosityLevel,
};
use color_eyre::{eyre::eyre, Result};
use colored::Colorize;
use semver::Version;
use sn_peers_acquisition::{get_peers_from_args, PeersArgs};
use sn_releases::{ReleaseType, SafeReleaseRepositoryInterface};
use sn_service_management::{
control::{ServiceControl, ServiceController},
FaucetService, NodeRegistry,
FaucetService, NodeRegistry, UpgradeOptions,
};
use sn_transfers::get_faucet_data_dir;
use std::path::PathBuf;
Expand Down Expand Up @@ -131,3 +133,70 @@ pub async fn stop(verbosity: VerbosityLevel) -> Result<()> {

Err(eyre!("The faucet service has not been added yet"))
}

pub async fn upgrade(
do_not_start: bool,
force: bool,
provided_env_variables: Option<Vec<(String, String)>>,
url: Option<String>,
version: Option<String>,
verbosity: VerbosityLevel,
) -> Result<()> {
if !is_running_as_root() {
return Err(eyre!("The upgrade command must run as the root user"));
}

let mut node_registry = NodeRegistry::load(&config::get_node_registry_path()?)?;
if node_registry.faucet.is_none() {
println!("No faucet service has been created yet. No upgrade required.");
return Ok(());
}

if verbosity != VerbosityLevel::Minimal {
println!("=================================================");
println!(" Upgrade Faucet Service ");
println!("=================================================");
}

let (upgrade_bin_path, target_version) =
download_and_get_upgrade_bin_path(ReleaseType::Faucet, url, version).await?;
let faucet = node_registry.faucet.clone().unwrap();

if !force {
let current_version = Version::parse(&faucet.version)?;
if target_version <= current_version {
println!(
"{} The faucet is already at the latest version",
"✓".green()
);
return Ok(());
}
}

let env_variables = if provided_env_variables.is_some() {
&provided_env_variables
} else {
&node_registry.environment_variables
};
let options = UpgradeOptions {
bootstrap_peers: node_registry.bootstrap_peers.clone(),
env_variables: env_variables.clone(),
force,
start_service: !do_not_start,
target_bin_path: upgrade_bin_path.clone(),
target_version: target_version.clone(),
};
let service = FaucetService::new(faucet.clone(), Box::new(ServiceController {}));
let mut service_manager =
ServiceManager::new(service, Box::new(ServiceController {}), verbosity);

match service_manager.upgrade(options).await {
Ok(upgrade_result) => {
node_registry.faucet = Some(service_manager.service.service_data);
print_upgrade_summary(vec![("faucet".to_string(), upgrade_result)]);
node_registry.save()?;
Ok(())
}
Err(e) => Err(eyre!("Upgrade failed: {e}")),
}
}
63 changes: 63 additions & 0 deletions sn_node_manager/src/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ pub mod node;

use crate::helpers::download_and_extract_release;
use color_eyre::{eyre::eyre, Result};
use colored::Colorize;
use semver::Version;
use sn_releases::{ReleaseType, SafeReleaseRepositoryInterface};
use sn_service_management::UpgradeResult;
use std::{
path::PathBuf,
process::{Command, Stdio},
Expand All @@ -29,6 +32,66 @@ pub fn is_running_as_root() -> bool {
true
}

pub async fn download_and_get_upgrade_bin_path(
release_type: ReleaseType,
url: Option<String>,
version: Option<String>,
) -> Result<(PathBuf, Version)> {
let release_repo = <dyn SafeReleaseRepositoryInterface>::default_config();
if let Some(version) = version {
let (upgrade_bin_path, version) =
download_and_extract_release(release_type, None, Some(version), &*release_repo).await?;
Ok((upgrade_bin_path, Version::parse(&version)?))
} else if let Some(url) = url {
let (upgrade_bin_path, version) =
download_and_extract_release(release_type, Some(url), None, &*release_repo).await?;
Ok((upgrade_bin_path, Version::parse(&version)?))
} else {
println!("Retrieving latest version of safenode...");
let latest_version = release_repo
.get_latest_version(&ReleaseType::Safenode)
.await?;
let latest_version = Version::parse(&latest_version)?;
println!("Latest version is {latest_version}");
let (upgrade_bin_path, _) = download_and_extract_release(
ReleaseType::Safenode,
None,
Some(latest_version.to_string()),
&*release_repo,
)
.await?;
Ok((upgrade_bin_path, latest_version))
}
}

pub fn print_upgrade_summary(upgrade_summary: Vec<(String, UpgradeResult)>) {
println!("Upgrade summary:");
for (service_name, upgrade_result) in upgrade_summary {
match upgrade_result {
UpgradeResult::NotRequired => {
println!("- {} did not require an upgrade", service_name);
}
UpgradeResult::Upgraded(previous_version, new_version) => {
println!(
"{} {} upgraded from {previous_version} to {new_version}",
"✓".green(),
service_name
);
}
UpgradeResult::Forced(previous_version, target_version) => {
println!(
"{} Forced {} version change from {previous_version} to {target_version}.",
"✓".green(),
service_name
);
}
UpgradeResult::Error(msg) => {
println!("{} {} was not upgraded: {}", "✕".red(), service_name, msg);
}
}
}
}

pub async fn get_bin_path(
build: bool,
path: Option<PathBuf>,
Expand Down
Loading
Loading