Files
nym/tools/nymvisor/src/error.rs
T
2024-03-06 10:26:15 +01:00

436 lines
14 KiB
Rust

// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::upgrades::types::{CurrentVersionInfo, DigestAlgorithm, UpgradeInfo};
use nix::errno::Errno;
use nix::sys::signal::Signal;
use nym_async_file_watcher::NotifyError;
use nym_bin_common::build_information::BinaryBuildInformationOwned;
use std::ffi::OsString;
use std::io;
use std::num::ParseIntError;
use std::path::PathBuf;
use std::process::ExitStatus;
use thiserror::Error;
use url::Url;
#[derive(Debug, Error)]
pub(crate) enum NymvisorError {
#[error(
"failed to load config file for id {id} using path '{}'. detailed message: {source}", path.display()
)]
ConfigLoadFailure {
id: String,
path: PathBuf,
#[source]
source: io::Error,
},
#[error(
"failed to save config file for id {id} using path '{}'. detailed message: {source}", path.display()
)]
ConfigSaveFailure {
id: String,
path: PathBuf,
#[source]
source: io::Error,
},
#[error(
"failed to load upgrade info using path '{}'. detailed message: {source}", path.display()
)]
UpgradeInfoLoadFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error(
"failed to save upgrade info for upgrade '{name}' using path '{}'. detailed message: {source}", path.display()
)]
UpgradeInfoSaveFailure {
name: String,
path: PathBuf,
#[source]
source: io::Error,
},
#[error("there seem to be an upgrade-info.json file present without the associated binary for upgrade '{name}' at path {}", path.display())]
UpgradeInfoWithNoBinary { name: String, path: PathBuf },
#[error("there seem to already exist the upgrade-plan.json at {}, but it has mismatched current information. The expected current is {current_name} but the saved one specifies {existing_name}", path.display())]
PreexistingUpgradePlan {
path: PathBuf,
current_name: String,
existing_name: String,
},
#[error(
"failed to load upgrade plan using path '{}'. detailed message: {source}", path.display()
)]
UpgradePlanLoadFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error(
"failed to save upgrade plan using path '{}'. detailed message: {source}", path.display()
)]
UpgradePlanSaveFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error(
"failed to load upgrade history using path '{}'. detailed message: {source}", path.display()
)]
UpgradeHistoryLoadFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error(
"failed to save upgrade history using path '{}'. detailed message: {source}", path.display()
)]
UpgradeHistorySaveFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error(
"failed to load current version information using path '{}'. detailed message: {source}", path.display()
)]
CurrentVersionInfoLoadFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error(
"failed to save current version information using path '{}'. detailed message: {source}", path.display()
)]
CurrentVersionInfoSaveFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error(
"the current version information does not match the expected `current/upgrade-info.json`.\n\
The daemon version is:\n{current_version_info:#?}\n\
While the stored info point to:\n{current_info:#?}"
)]
UnexpectedCurrentVersionInfo {
current_info: Box<UpgradeInfo>,
current_version_info: Box<CurrentVersionInfo>,
},
#[error(
"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_info: Box<BinaryBuildInformationOwned>,
stored_info: Box<BinaryBuildInformationOwned>,
},
#[error("the daemon for upgrade '{upgrade_name}' has version {daemon_version} while {expected} was expected instead")]
UnexpectedUpgradeDaemonVersion {
upgrade_name: String,
daemon_version: String,
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,
libc_code: Errno,
},
#[error("could not create the lock file at {} to perform binary upgrade: {source}", path.display())]
LockFileCreationFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error("could not remove the lock file at {} after performing binary upgrade: {source}", path.display())]
LockFileRemovalFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error("could not create the backup file at {} before performing binary upgrade: {source}", path.display())]
BackupFileCreationFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error("could not tar backup directory {} to {}: {source}", data_source.display(), path.display())]
BackupTarDirFailure {
path: PathBuf,
data_source: PathBuf,
#[source]
source: io::Error,
},
#[error("could not tar backup file {} to {}: {source}", data_source.display(), path.display())]
BackupTarFileFailure {
path: PathBuf,
data_source: PathBuf,
#[source]
source: io::Error,
},
#[error("could not finalize the tar backup file at {}: {source}", path.display())]
BackupTarFinalizationFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error("failed to initialise the path '{}': {source}", path.display())]
PathInitFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error("the provided env file was malformed: {source}")]
MalformedEnvFile {
#[from]
source: dotenvy::Error,
},
#[error("the value provided for environmental variable '{variable}' was not valid unicode: {value:?}")]
MalformedEnvVariable { variable: String, value: OsString },
#[error("the value provided for environmental boolean variable '{variable}': '{value}' is not a valid boolean")]
MalformedBoolEnvVariable { variable: String, value: String },
#[error("the value provided for environmental duration variable '{variable}': '{value}' is not a valid duration: {source}")]
MalformedDurationEnvVariable {
variable: String,
value: String,
#[source]
source: humantime::DurationError,
},
#[error("the value provided for environmental numerical variable '{variable}': '{value}' is not a valid number: {source}")]
MalformedNumberEnvVariable {
variable: String,
value: String,
#[source]
source: ParseIntError,
},
#[error("the value provided for environmental Url '{variable}': '{value}' is not a valid number: {source}")]
MalformedUrlEnvVariable {
variable: String,
value: String,
#[source]
source: url::ParseError,
},
#[error("failed to copy daemon binary from '{}' to '{}': {source}", source_path.display(), target_path.display())]
DaemonBinaryCopyFailure {
source_path: PathBuf,
target_path: PathBuf,
#[source]
source: io::Error,
},
#[error("failed to create symlink from '{}' to '{}': {source}", source_path.display(), target_path.display())]
SymlinkCreationFailure {
source_path: PathBuf,
target_path: PathBuf,
#[source]
source: io::Error,
},
#[error("failed to remove symlink at '{}': {source}", path.display())]
SymlinkRemovalFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error("the value of daemon home has to be provided by either `--daemon-home` flag or `$DAEMON_HOME` environmental variable")]
DaemonHomeUnavailable,
#[error("failed to obtain build information from the daemon executable ('{}'): {source}", binary_path.display())]
DaemonBuildInformationFailure {
binary_path: PathBuf,
#[source]
source: io::Error,
},
#[error("failed to parse build information from the daemon executable: {source}")]
DaemonBuildInformationParseFailure {
#[source]
source: serde_json::Error,
},
#[error("the daemon execution has failed with the following exit code: {exit_code:?}. the associated signal code: {signal_code:?}. the core was dumped: {core_dumped}")]
DaemonExecutionFailure {
// exit code of the process, if any
exit_code: Option<i32>,
// if the process was WIFSIGNALED, this returns WTERMSIG.
signal_code: Option<i32>,
core_dumped: bool,
},
#[error("the daemon execution has experienced an io failure: {source}")]
DaemonIoFailure {
#[source]
source: io::Error,
},
#[error("there was already a genesis binary present for {daemon_name} which was different that the one provided.\nProvided:\n{provided_genesis:#?}\nExisting:\n{existing_info:#?}")]
DuplicateDaemonGenesisBinary {
daemon_name: String,
existing_info: Box<BinaryBuildInformationOwned>,
provided_genesis: Box<BinaryBuildInformationOwned>,
},
#[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,
link: PathBuf,
expected_link: PathBuf,
},
#[error("failed to send to send {signal} to the daemon process: {source}")]
DaemonSignalFailure {
signal: Signal,
#[source]
source: nix::Error,
},
#[error("failed to watch for changes in the upgrade-plan.json: {source}")]
UpgradePlanFileWatchFailure {
#[from]
source: NotifyError,
},
#[error("failed to query the upstream url ('{url}'): {source}")]
UpstreamQueryFailure {
url: Url,
#[source]
source: reqwest::Error,
},
#[error(
"attempted to perform binary upgrade with no upgrades queued up in the upgrade plan file"
)]
NoQueuedUpgrades,
#[error("could not find the upgrade binary at {} while the binary download is disabled", path.display())]
NoUpgradeBinaryWithDisabledDownload { path: PathBuf },
#[error("upgrade '{upgrade_name}' does not have any valid download URLs for the current arch '{arch}'. The available arches are: {available:?}")]
NoDownloadUrls {
upgrade_name: String,
arch: String,
available: Vec<String>,
},
#[error("failed to download the upgrade binary from '{url}': {source}")]
UpgradeDownloadFailure {
url: Url,
#[source]
source: reqwest::Error,
},
#[error("failed to verify checksum for upgrade '{upgrade_name}' using {algorithm}. Got '{encoded_checksum}' while expected '{expected_checksum}'")]
DownloadChecksumFailure {
upgrade_name: String,
encoded_checksum: String,
expected_checksum: String,
algorithm: DigestAlgorithm,
},
#[error("could not calculate checksum for downloaded file at '{}': {source}", path.display())]
ChecksumCalculationFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error("download information for upgrade '{upgrade_name}' is missing checksum")]
MissingDownloadChecksum { upgrade_name: String },
#[error("failed to create daemon binary at {}: {source}", path.display())]
DaemonBinaryCreationFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error("the daemon has reached the maximum number of startup failures ({failures})")]
DaemonMaximumStartupFailures { failures: usize },
#[error("the daemon restart on failure is disabled")]
DisabledRestartOnFailure,
#[error("failed to read directory content of nymvisor instances at {}: {source}", path.display())]
InstancesReadFailure {
path: PathBuf,
#[source]
source: io::Error,
},
#[error("could not load the default config file as there isn't a single nymvisor instance initiated (there are {instances}). please specify either $NYMVISOR_CONFIG_PATH or $NYMVISOR_ID")]
NotSingleton { instances: usize },
#[error("failed to crate tokio's runtime: {source}")]
RuntimeCreationFailure { source: io::Error },
}
impl From<ExitStatus> for NymvisorError {
fn from(value: ExitStatus) -> Self {
use std::os::unix::prelude::ExitStatusExt;
assert!(!value.success());
NymvisorError::DaemonExecutionFailure {
exit_code: value.code(),
signal_code: value.signal(),
core_dumped: value.core_dumped(),
}
}
}