clippy and final missing features
This commit is contained in:
@@ -35,26 +35,25 @@ pub(crate) struct Args {
|
||||
|
||||
/// Indicate that this command should only add binary to an *existing* scheduled upgrade
|
||||
#[arg(long)]
|
||||
#[deprecated(note = "need to implement")]
|
||||
add_binary: bool,
|
||||
|
||||
/// Force the upgrade to happen immediately
|
||||
#[arg(long, group = "time")]
|
||||
#[arg(long, group = "time", conflicts_with = "add_binary")]
|
||||
now: bool,
|
||||
|
||||
/// Specifies the publish date metadata field of this upgrade.
|
||||
/// If unset, the current time will be used.
|
||||
#[arg(long, value_parser = parse_rfc3339_upgrade_time)]
|
||||
#[arg(long, value_parser = parse_rfc3339_upgrade_time, conflicts_with = "add_binary")]
|
||||
publish_date: Option<OffsetDateTime>,
|
||||
|
||||
/// Specifies the time at which the provided upgrade will be performed (RFC3339 formatted).
|
||||
/// If left unset, the upgrade will be performed in 15min
|
||||
#[arg(long, value_parser = parse_rfc3339_upgrade_time, group = "time")]
|
||||
#[arg(long, value_parser = parse_rfc3339_upgrade_time, group = "time", conflicts_with = "add_binary")]
|
||||
upgrade_time: Option<OffsetDateTime>,
|
||||
|
||||
/// Specifies delay until the provided upgrade is going to get performed.
|
||||
/// If let unset, the upgrade will be performed in 15min
|
||||
#[arg(long, value_parser = humantime::parse_duration, group = "time")]
|
||||
#[arg(long, value_parser = humantime::parse_duration, group = "time", conflicts_with = "add_binary")]
|
||||
upgrade_delay: Option<Duration>,
|
||||
|
||||
#[clap(short, long, default_value_t = OutputFormat::default())]
|
||||
@@ -87,52 +86,54 @@ pub(crate) fn execute(args: Args) -> Result<(), NymvisorError> {
|
||||
if env.daemon_name.is_none() {
|
||||
env.daemon_name = Some(bin_info.binary_name.clone());
|
||||
}
|
||||
|
||||
let config = try_load_current_config(&env)?;
|
||||
|
||||
let mut current_upgrade_plan = UpgradePlan::try_load(config.upgrade_plan_filepath())?;
|
||||
|
||||
let upgrade_time = args.determine_upgrade_time();
|
||||
let upgrade_info = UpgradeInfo {
|
||||
manual: false,
|
||||
name: args.upgrade_name,
|
||||
notes: "manually added via 'add-upgrade' command".to_string(),
|
||||
publish_date: Some(args.publish_date.unwrap_or(OffsetDateTime::now_utc())),
|
||||
version: bin_info.build_version.clone(),
|
||||
platforms: Default::default(),
|
||||
upgrade_time,
|
||||
binary_details: Some(bin_info),
|
||||
};
|
||||
|
||||
let upgrade_info_path = config.upgrade_dir(&upgrade_info.name);
|
||||
if upgrade_info_path.exists() {
|
||||
// TODO: maybe just copy binary?
|
||||
todo!()
|
||||
}
|
||||
|
||||
// TODO: check if upgrade-plan already contains this upgrade
|
||||
|
||||
// if upgrade_info_path.exists() && !args.force {
|
||||
// return Err(NymvisorError::ExistingUpgrade {
|
||||
// name: upgrade_info.name,
|
||||
// path: upgrade_info_path,
|
||||
// });
|
||||
// }
|
||||
let upgrade_binary_path = config.upgrade_binary_dir(&upgrade_info.name);
|
||||
if upgrade_binary_path.exists() && !args.force {
|
||||
let upgrade_info_path = config.upgrade_info_filepath(&args.upgrade_name);
|
||||
let bin_path = config.upgrade_binary(&args.upgrade_name);
|
||||
if bin_path.exists() && !args.force {
|
||||
return Err(NymvisorError::ExistingUpgrade {
|
||||
name: upgrade_info.name,
|
||||
path: upgrade_binary_path,
|
||||
name: args.upgrade_name,
|
||||
path: bin_path,
|
||||
});
|
||||
}
|
||||
|
||||
init_path(upgrade_binary_path)?;
|
||||
copy_binary(
|
||||
&args.daemon_binary,
|
||||
config.upgrade_binary(&upgrade_info.name),
|
||||
)?;
|
||||
upgrade_info.save(config.upgrade_info_filepath(&upgrade_info.name))?;
|
||||
current_upgrade_plan.insert_new_upgrade(upgrade_info)?;
|
||||
// if we're just adding the binary, the upgrade plan MUST already exist,
|
||||
// otherwise it MUSTN'T exist (unless --force is used)
|
||||
if args.add_binary {
|
||||
let upgrade_info = UpgradeInfo::try_load(upgrade_info_path)?;
|
||||
upgrade_info.ensure_matches_bin_info(&bin_info)?;
|
||||
} else {
|
||||
if upgrade_info_path.exists() && !args.force {
|
||||
return Err(NymvisorError::ExistingUpgradeInfo {
|
||||
name: args.upgrade_name,
|
||||
path: upgrade_info_path,
|
||||
});
|
||||
}
|
||||
|
||||
let mut current_upgrade_plan = UpgradePlan::try_load(config.upgrade_plan_filepath())?;
|
||||
let upgrade_info = UpgradeInfo {
|
||||
manual: false,
|
||||
name: args.upgrade_name.clone(),
|
||||
notes: "manually added via 'add-upgrade' command".to_string(),
|
||||
publish_date: Some(args.publish_date.unwrap_or(OffsetDateTime::now_utc())),
|
||||
version: bin_info.build_version.clone(),
|
||||
platforms: Default::default(),
|
||||
upgrade_time: args.determine_upgrade_time(),
|
||||
binary_details: Some(bin_info),
|
||||
};
|
||||
|
||||
if current_upgrade_plan.has_planned_by_name(&args.upgrade_name) {
|
||||
return Err(NymvisorError::UpgradePlanWithNoInfo {
|
||||
name: args.upgrade_name,
|
||||
});
|
||||
}
|
||||
|
||||
init_path(config.upgrade_binary_dir(&args.upgrade_name))?;
|
||||
upgrade_info.save(config.upgrade_info_filepath(&upgrade_info.name))?;
|
||||
current_upgrade_plan.insert_new_upgrade(upgrade_info)?;
|
||||
}
|
||||
|
||||
copy_binary(&args.daemon_binary, bin_path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ impl Cli {
|
||||
setup_env(&self.config_env_file)?;
|
||||
|
||||
match self.command {
|
||||
Commands::Init(args) => init::execute(args),
|
||||
Commands::Init(args) => init::execute(*args),
|
||||
Commands::Run(args) => run::execute(args),
|
||||
Commands::BuildInfo(args) => build_info::execute(args),
|
||||
Commands::DaemonBuildInfo(args) => daemon_build_info::execute(args),
|
||||
@@ -56,7 +56,7 @@ impl Cli {
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub(crate) enum Commands {
|
||||
/// Initialise a nymvisor instance with persistent Config.toml file.
|
||||
Init(init::Args),
|
||||
Init(Box<init::Args>),
|
||||
|
||||
/// Run the associated daemon with the preconfigured settings.
|
||||
Run(run::Args),
|
||||
|
||||
@@ -8,7 +8,10 @@ use crate::tasks::launcher::DaemonLauncher;
|
||||
use crate::tasks::upgrade_plan_watcher::start_upgrade_plan_watcher;
|
||||
use crate::tasks::upstream_poller::UpstreamPoller;
|
||||
use nym_bin_common::logging::setup_tracing_logger;
|
||||
use std::future::Future;
|
||||
use std::time::Duration;
|
||||
use tokio::runtime;
|
||||
use tokio::time::timeout;
|
||||
use tracing::{error, info};
|
||||
|
||||
#[derive(clap::Args, Debug)]
|
||||
@@ -27,8 +30,8 @@ pub(crate) fn execute(args: Args) -> Result<(), NymvisorError> {
|
||||
|
||||
info!("starting nymvisor for {}", config.daemon.name);
|
||||
|
||||
// TODO: experiment with the minimal runtime
|
||||
// look at futures::executor::LocalPool
|
||||
// TODO: experiment with other minimal runtimes, maybe futures::executor::LocalPool
|
||||
//
|
||||
// well, if the creation of the runtime failed, there isn't much we could do
|
||||
#[allow(clippy::expect_used)]
|
||||
let rt = runtime::Builder::new_current_thread()
|
||||
@@ -62,11 +65,18 @@ pub(crate) fn execute(args: Args) -> Result<(), NymvisorError> {
|
||||
upstream_poller_handle.abort();
|
||||
}
|
||||
|
||||
// TODO: add timeouts and error handling here
|
||||
// TODO2: maybe we need to make those fuse futures?
|
||||
watcher_handle.await;
|
||||
upstream_poller_handle.await;
|
||||
wait_for_task_termination(watcher_handle, "Upgrade plan watcher").await;
|
||||
wait_for_task_termination(upstream_poller_handle, "Upstream poller").await;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
async fn wait_for_task_termination<F: Future>(task: F, name: &str) {
|
||||
match timeout(Duration::from_secs(2), task).await {
|
||||
Ok(_) => info!("{name} has finished execution"),
|
||||
Err(_timeout) => {
|
||||
error!("{name} task has timed out and has not shutdown gracefully")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,10 +206,6 @@ impl Config {
|
||||
Self::read_from_path(path)
|
||||
}
|
||||
|
||||
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
|
||||
Self::read_from_path(default_config_filepath(id))
|
||||
}
|
||||
|
||||
pub fn default_location(&self) -> PathBuf {
|
||||
default_config_filepath(&self.nymvisor.id)
|
||||
}
|
||||
@@ -223,6 +219,8 @@ impl Config {
|
||||
save_formatted_config_to_file(self, path)
|
||||
}
|
||||
|
||||
// this code will be needed for config upgrades
|
||||
#[allow(dead_code)]
|
||||
pub fn try_save(&self) -> io::Result<()> {
|
||||
if let Some(save_location) = &self.save_path {
|
||||
save_formatted_config_to_file(self, save_location)
|
||||
@@ -253,7 +251,7 @@ impl Config {
|
||||
|
||||
// e.g. $HOME/.nym/nym-api/<id>/nymvisor/current-version.json
|
||||
pub fn current_daemon_version_filepath(&self) -> PathBuf {
|
||||
todo!()
|
||||
self.daemon_nymvisor_dir().join(CURRENT_VERSION_FILENAME)
|
||||
}
|
||||
|
||||
// e.g. $HOME/.nym/nymvisors/data/nym-api/
|
||||
|
||||
@@ -84,10 +84,17 @@ impl Daemon {
|
||||
|
||||
#[instrument(skip(self), fields(self.executable_path = ?self.executable_path))]
|
||||
pub(crate) fn verify_binary(&self) -> Result<(), NymvisorError> {
|
||||
let metadata = fs::metadata(&self.executable_path).expect("error handling");
|
||||
let metadata = fs::metadata(&self.executable_path).map_err(|source| {
|
||||
NymvisorError::MetadataReadFailure {
|
||||
path: self.executable_path.clone(),
|
||||
source,
|
||||
}
|
||||
})?;
|
||||
|
||||
if !metadata.is_file() {
|
||||
todo!("error not a file")
|
||||
return Err(NymvisorError::DaemonNotAFile {
|
||||
path: self.executable_path.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
let mut permissions = metadata.permissions();
|
||||
@@ -100,7 +107,12 @@ impl Daemon {
|
||||
let new_mode = mode | 0o111; // Set the three execute bits to on (a+x).
|
||||
permissions.set_mode(new_mode);
|
||||
|
||||
fs::set_permissions(&self.executable_path, permissions).expect("error handling");
|
||||
fs::set_permissions(&self.executable_path, permissions).map_err(|source| {
|
||||
NymvisorError::DaemonPermissionFailure {
|
||||
path: self.executable_path.clone(),
|
||||
source,
|
||||
}
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -130,13 +130,13 @@ While the stored info point to:\n{current_info:#?}"
|
||||
},
|
||||
|
||||
#[error(
|
||||
"the current daemon build does not match the expected value in `current-version.info.json`.\n\
|
||||
The daemon version is:\n{daemon_version:#?}\n\
|
||||
While the stored info point to:\n{current_version_info:#?}"
|
||||
"the current daemon build information does not match the expected stored value.\n\
|
||||
The daemon build is:\n{daemon_info:#?}\n\
|
||||
While the stored info point to:\n{stored_info:#?}"
|
||||
)]
|
||||
UnexpectedDaemonBuild {
|
||||
daemon_version: Box<BinaryBuildInformationOwned>,
|
||||
current_version_info: Box<BinaryBuildInformationOwned>,
|
||||
daemon_info: Box<BinaryBuildInformationOwned>,
|
||||
stored_info: Box<BinaryBuildInformationOwned>,
|
||||
},
|
||||
|
||||
#[error("the daemon for upgrade '{upgrade_name}' has version {daemon_version} while {expected} was expected instead")]
|
||||
@@ -146,6 +146,23 @@ While the stored info point to:\n{current_version_info:#?}"
|
||||
expected: String,
|
||||
},
|
||||
|
||||
#[error("the provided daemon at {} is not a file", path.display())]
|
||||
DaemonNotAFile { path: PathBuf },
|
||||
|
||||
#[error("could not read daemon's metadata at {}: {source}", path.display())]
|
||||
MetadataReadFailure {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
|
||||
#[error("could not adjust permission of the daemon at: {}: {source}", path.display())]
|
||||
DaemonPermissionFailure {
|
||||
path: PathBuf,
|
||||
#[source]
|
||||
source: io::Error,
|
||||
},
|
||||
|
||||
#[error("could not acquire the lock at {} to perform binary upgrade with error code {libc_code}. It is either held by another process or this nymvisor has experienced a critical failure during previous upgrade attempt", lock_path.display())]
|
||||
UnableToAcquireUpgradePlanLock {
|
||||
lock_path: PathBuf,
|
||||
@@ -304,9 +321,15 @@ While the stored info point to:\n{current_version_info:#?}"
|
||||
provided_genesis: Box<BinaryBuildInformationOwned>,
|
||||
},
|
||||
|
||||
#[error("there already exist upgrade binary for '{name}' at: {}. if you want to ovewrite its content, use --force flag", path.display())]
|
||||
#[error("there already exist upgrade binary for '{name}' at: {}. if you want to overwrite its content, use --force flag", path.display())]
|
||||
ExistingUpgrade { name: String, path: PathBuf },
|
||||
|
||||
#[error("there already exist upgrade information for '{name}' at: {}. if you want to overwrite its content, use --force flag", path.display())]
|
||||
ExistingUpgradeInfo { name: String, path: PathBuf },
|
||||
|
||||
#[error("the current upgrade-plan.json has planned upgrade for '{name}', but no corresponding upgrade-info.json file could be found")]
|
||||
UpgradePlanWithNoInfo { name: String },
|
||||
|
||||
#[error("there was already a symlink for the 'current' binary of {daemon_name}. it's pointing to {} while we needed to create one to {}", link.display(), expected_link.display())]
|
||||
ExistingCurrentSymlink {
|
||||
daemon_name: String,
|
||||
|
||||
@@ -146,8 +146,8 @@ impl DaemonLauncher {
|
||||
current_info.ensure_matches(&expected_version)?;
|
||||
if expected_version.binary_details != daemon_info {
|
||||
return Err(NymvisorError::UnexpectedDaemonBuild {
|
||||
daemon_version: Box::new(daemon_info),
|
||||
current_version_info: Box::new(expected_version.binary_details),
|
||||
daemon_info: Box::new(daemon_info),
|
||||
stored_info: Box::new(expected_version.binary_details),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -88,13 +88,7 @@ pub(crate) async fn perform_upgrade(config: &Config) -> Result<UpgradeResult, Ny
|
||||
tmp_daemon.verify_binary()?;
|
||||
|
||||
let new_bin_info = tmp_daemon.get_build_information()?;
|
||||
if new_bin_info.build_version != next.version {
|
||||
return Err(NymvisorError::UnexpectedUpgradeDaemonVersion {
|
||||
upgrade_name,
|
||||
daemon_version: new_bin_info.build_version,
|
||||
expected: next.version,
|
||||
});
|
||||
}
|
||||
next.ensure_matches_bin_info(&new_bin_info)?;
|
||||
|
||||
// update the 'current-version-history.json'
|
||||
CurrentVersionInfo {
|
||||
@@ -106,6 +100,7 @@ pub(crate) async fn perform_upgrade(config: &Config) -> Result<UpgradeResult, Ny
|
||||
.save(config.current_daemon_version_filepath())?;
|
||||
|
||||
// update the 'upgrade-plan.json'
|
||||
plan.set_current(next.clone());
|
||||
plan.update_on_disk()?;
|
||||
|
||||
// update the 'upgrade-history.json'
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::serde_helpers::{base64, option_offsetdatetime};
|
||||
use crate::config::GENESIS_DIR;
|
||||
use crate::error::NymvisorError;
|
||||
use crate::helpers::{calculate_file_checksum, init_path};
|
||||
use crate::upgrades::download::os_arch;
|
||||
@@ -119,9 +118,14 @@ impl UpgradePlan {
|
||||
false
|
||||
}
|
||||
|
||||
// pub(crate) fn update_current(&mut self) -> Result<(), NymvisorError> {
|
||||
//
|
||||
// }
|
||||
pub(crate) fn has_planned_by_name(&self, upgrade_name: &str) -> bool {
|
||||
for planned in &self.queued_up {
|
||||
if planned.name == upgrade_name {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) fn save_new<P: AsRef<Path>>(&self, path: P) -> Result<(), NymvisorError> {
|
||||
debug_assert!(self._save_path.is_none());
|
||||
@@ -280,9 +284,9 @@ impl UpgradeInfo {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn is_genesis(&self) -> bool {
|
||||
self.name == GENESIS_DIR
|
||||
}
|
||||
// pub(crate) fn is_genesis(&self) -> bool {
|
||||
// self.name == GENESIS_DIR
|
||||
// }
|
||||
|
||||
pub(crate) fn get_download_url(&self) -> Result<&DownloadUrl, NymvisorError> {
|
||||
if let Some(download_url) = self.platforms.get(&os_arch()) {
|
||||
@@ -319,6 +323,29 @@ impl UpgradeInfo {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn ensure_matches_bin_info(
|
||||
&self,
|
||||
info: &BinaryBuildInformationOwned,
|
||||
) -> Result<(), NymvisorError> {
|
||||
if let Some(self_info) = &self.binary_details {
|
||||
if self_info != info {
|
||||
return Err(NymvisorError::UnexpectedDaemonBuild {
|
||||
daemon_info: Box::new(info.clone()),
|
||||
stored_info: Box::new(self_info.clone()),
|
||||
});
|
||||
}
|
||||
}
|
||||
if self.version != info.build_version {
|
||||
return Err(NymvisorError::UnexpectedUpgradeDaemonVersion {
|
||||
upgrade_name: self.name.clone(),
|
||||
daemon_version: info.build_version.clone(),
|
||||
expected: self.version.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
||||
Reference in New Issue
Block a user