moved backup to separate module + standalone file support
This commit is contained in:
@@ -26,8 +26,6 @@ pub(crate) const DEFAULT_UPSTREAM_POLLING_RATE: Duration = Duration::from_secs(6
|
||||
pub(crate) const DEFAULT_BASE_UPSTREAM_UPGRADE_INFO_SOURCE: &str =
|
||||
"https://nymtech.net/.wellknown/";
|
||||
|
||||
pub(crate) const DAEMON_CONFIG_DIR: &str = "config";
|
||||
pub(crate) const DAEMON_DATA_DIR: &str = "data";
|
||||
pub(crate) const UPGRADE_PLAN_FILENAME: &str = "upgrade-plan.json";
|
||||
pub(crate) const UPGRADE_HISTORY_FILENAME: &str = "upgrade-history.json";
|
||||
pub(crate) const UPGRADE_LOCK_FILENAME: &str = "upgrade.lock";
|
||||
@@ -238,16 +236,6 @@ impl Config {
|
||||
self.daemon.home.join(NYMVISOR_DIR)
|
||||
}
|
||||
|
||||
// e.g. $HOME/.nym/nym-api/<id>/data
|
||||
pub fn daemon_data_dir(&self) -> PathBuf {
|
||||
self.daemon.home.join(DAEMON_DATA_DIR)
|
||||
}
|
||||
|
||||
// e.g. $HOME/.nym/nym-api/<id>/config
|
||||
pub fn daemon_config_dir(&self) -> PathBuf {
|
||||
self.daemon.home.join(DAEMON_CONFIG_DIR)
|
||||
}
|
||||
|
||||
// e.g. $HOME/.nym/nym-apis/<id>/nymvisor/backups
|
||||
pub fn daemon_backup_dir(&self) -> PathBuf {
|
||||
if let Some(backup_dir) = &self.daemon.debug.backup_data_directory {
|
||||
|
||||
@@ -128,7 +128,15 @@ pub(crate) enum NymvisorError {
|
||||
},
|
||||
|
||||
#[error("could not tar backup directory {} to {}: {source}", data_source.display(), path.display())]
|
||||
BackupTarFailure {
|
||||
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]
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::NYMVISOR_DIR;
|
||||
use crate::error::NymvisorError;
|
||||
use flate2::write::GzEncoder;
|
||||
use flate2::Compression;
|
||||
use std::fs;
|
||||
use std::fs::{DirEntry, File};
|
||||
use std::path::{Path, PathBuf};
|
||||
use time::{format_description, OffsetDateTime};
|
||||
use tracing::info;
|
||||
|
||||
fn generate_backup_filename() -> String {
|
||||
// safety: this expect is fine as we're using a constant formatter.
|
||||
#[allow(clippy::expect_used)]
|
||||
let format = format_description::parse(
|
||||
"[year]-[month]-[day]-[hour][minute][second][subsecond digits:3]",
|
||||
)
|
||||
.expect("our time formatter is malformed");
|
||||
#[allow(clippy::expect_used)]
|
||||
let now = OffsetDateTime::now_utc()
|
||||
.format(&format)
|
||||
.expect("our time formatter failed to format the current time");
|
||||
|
||||
format!("backup-{now}-preupgrade.tar.gz")
|
||||
}
|
||||
|
||||
pub(crate) struct BackupBuilder {
|
||||
tar_builder: tar::Builder<GzEncoder<File>>,
|
||||
backup_filepath: PathBuf,
|
||||
}
|
||||
|
||||
impl BackupBuilder {
|
||||
pub(crate) fn new<P: AsRef<Path>>(backup_directory: P) -> Result<Self, NymvisorError> {
|
||||
let backup_filepath = backup_directory.as_ref().join(generate_backup_filename());
|
||||
|
||||
// create the backup file
|
||||
let backup_file = fs::File::create(&backup_filepath).map_err(|source| {
|
||||
NymvisorError::BackupFileCreationFailure {
|
||||
path: backup_filepath.clone(),
|
||||
source,
|
||||
}
|
||||
})?;
|
||||
|
||||
let enc = GzEncoder::new(backup_file, Compression::default());
|
||||
let tar_builder = tar::Builder::new(enc);
|
||||
Ok(BackupBuilder {
|
||||
tar_builder,
|
||||
backup_filepath,
|
||||
})
|
||||
}
|
||||
|
||||
fn backup_subdir(&mut self, dir_entry: DirEntry) -> Result<(), NymvisorError> {
|
||||
let path = dir_entry.path();
|
||||
let filename = dir_entry.file_name();
|
||||
info!(
|
||||
"attempting to put {} into the backup tar file",
|
||||
path.display()
|
||||
);
|
||||
|
||||
if dir_entry.file_name() == NYMVISOR_DIR {
|
||||
info!("skipping the /{NYMVISOR_DIR}...");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if path.is_dir() {
|
||||
self.tar_builder
|
||||
.append_dir_all(filename, &path)
|
||||
.map_err(|source| NymvisorError::BackupTarDirFailure {
|
||||
path: self.backup_filepath.clone(),
|
||||
data_source: path,
|
||||
source,
|
||||
})
|
||||
} else {
|
||||
self.tar_builder
|
||||
.append_path_with_name(&path, filename)
|
||||
.map_err(|source| NymvisorError::BackupTarFileFailure {
|
||||
path: self.backup_filepath.clone(),
|
||||
data_source: path,
|
||||
source,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn backup_daemon_home<P: AsRef<Path>>(
|
||||
mut self,
|
||||
daemon_home: P,
|
||||
) -> Result<(), NymvisorError> {
|
||||
let home = daemon_home.as_ref();
|
||||
let home_entry =
|
||||
fs::read_dir(home).map_err(|source| NymvisorError::BackupTarDirFailure {
|
||||
path: self.backup_filepath.clone(),
|
||||
data_source: home.to_path_buf(),
|
||||
source,
|
||||
})?;
|
||||
|
||||
for path in home_entry {
|
||||
let dir_entry = path.map_err(|source| NymvisorError::BackupTarDirFailure {
|
||||
path: self.backup_filepath.clone(),
|
||||
data_source: home.to_path_buf(),
|
||||
source,
|
||||
})?;
|
||||
self.backup_subdir(dir_entry)?;
|
||||
}
|
||||
self.finish()
|
||||
}
|
||||
|
||||
fn finish(mut self) -> Result<(), NymvisorError> {
|
||||
self.tar_builder
|
||||
.finish()
|
||||
.map_err(|source| NymvisorError::BackupTarFinalizationFailure {
|
||||
path: self.backup_filepath,
|
||||
source,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,26 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::{Config, DAEMON_CONFIG_DIR, DAEMON_DATA_DIR};
|
||||
use crate::config::Config;
|
||||
use crate::daemon::Daemon;
|
||||
use crate::error::NymvisorError;
|
||||
use crate::tasks::launcher::backup::BackupBuilder;
|
||||
use crate::upgrades::{
|
||||
types::{UpgradeInfo, UpgradePlan},
|
||||
upgrade_binary,
|
||||
};
|
||||
use async_file_watcher::FileWatcherEventReceiver;
|
||||
use flate2::write::GzEncoder;
|
||||
use flate2::Compression;
|
||||
use futures::future::{FusedFuture, OptionFuture};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use nym_task::signal::wait_for_signal;
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use time::format_description::well_known::Rfc3339;
|
||||
use time::{format_description, OffsetDateTime};
|
||||
use time::OffsetDateTime;
|
||||
use tokio::pin;
|
||||
use tokio::sync::Notify;
|
||||
use tokio::time::{sleep, Sleep};
|
||||
use tokio::time::sleep;
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
mod backup;
|
||||
|
||||
pub(crate) struct DaemonLauncher {
|
||||
config: Config,
|
||||
upgrade_plan_watcher: FileWatcherEventReceiver,
|
||||
@@ -34,8 +31,8 @@ impl DaemonLauncher {
|
||||
todo!()
|
||||
}
|
||||
|
||||
// the full upgrade process process, i.e. run until upgrade, do backup and perform the upgrade.
|
||||
// returns a boolean indicating whether the process should get restarted
|
||||
/// the full upgrade process process, i.e. run until upgrade, do backup and perform the upgrade.
|
||||
/// returns a boolean indicating whether the process should get restarted
|
||||
pub(crate) async fn run(&mut self, args: Vec<String>) -> Result<bool, NymvisorError> {
|
||||
let upgrade_available = self.wait_for_upgrade_or_termination(args.clone()).await?;
|
||||
if !upgrade_available {
|
||||
@@ -44,8 +41,9 @@ impl DaemonLauncher {
|
||||
|
||||
self.perform_backup()?;
|
||||
upgrade_binary(&self.config).await?;
|
||||
// if we ever wanted to introduce any pre-upgrade scripts like cosmovisor, they'd go here
|
||||
|
||||
todo!()
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// this function gets called whenever the file watcher detects changes in the upgrade plan file
|
||||
@@ -150,52 +148,7 @@ impl DaemonLauncher {
|
||||
}
|
||||
|
||||
fn perform_backup(&self) -> Result<(), NymvisorError> {
|
||||
// safety: this expect is fine as we're using a constant formatter.
|
||||
#[allow(clippy::expect_used)]
|
||||
let format = format_description::parse(
|
||||
"[year]-[month]-[day]-[hour][minute][second][subsecond digits:3]",
|
||||
)
|
||||
.expect("our time formatter is malformed");
|
||||
#[allow(clippy::expect_used)]
|
||||
let now = OffsetDateTime::now_utc()
|
||||
.format(&format)
|
||||
.expect("our time formatter failed to format the current time");
|
||||
|
||||
let backup_filepath = self
|
||||
.config
|
||||
.daemon_backup_dir()
|
||||
.join(format!("backup-{now}-preupgrade.tar.gz"));
|
||||
|
||||
// create the backup file
|
||||
let backup_file = fs::File::create(&backup_filepath).map_err(|source| {
|
||||
NymvisorError::BackupFileCreationFailure {
|
||||
path: backup_filepath.clone(),
|
||||
source,
|
||||
}
|
||||
})?;
|
||||
|
||||
let daemon_data = self.config.daemon_data_dir();
|
||||
let daemon_config = self.config.daemon_config_dir();
|
||||
let enc = GzEncoder::new(backup_file, Compression::default());
|
||||
let mut tar = tar::Builder::new(enc);
|
||||
tar.append_dir_all(DAEMON_DATA_DIR, &daemon_data)
|
||||
.map_err(|source| NymvisorError::BackupTarFailure {
|
||||
path: backup_filepath.clone(),
|
||||
data_source: daemon_data,
|
||||
source,
|
||||
})?;
|
||||
|
||||
tar.append_dir_all(DAEMON_CONFIG_DIR, &daemon_config)
|
||||
.map_err(|source| NymvisorError::BackupTarFailure {
|
||||
path: backup_filepath.clone(),
|
||||
data_source: daemon_config,
|
||||
source,
|
||||
})?;
|
||||
|
||||
tar.finish()
|
||||
.map_err(|source| NymvisorError::BackupTarFinalizationFailure {
|
||||
path: backup_filepath,
|
||||
source,
|
||||
})
|
||||
BackupBuilder::new(self.config.daemon_backup_dir())?
|
||||
.backup_daemon_home(&self.config.daemon.home)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user