moved backup to separate module + standalone file support

This commit is contained in:
Jędrzej Stuczyński
2023-11-09 10:35:11 +00:00
parent 56ecfa7e38
commit c04b617a55
4 changed files with 138 additions and 72 deletions
-12
View File
@@ -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 {
+9 -1
View File
@@ -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]
+117
View File
@@ -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,
})
}
}
+12 -59
View File
@@ -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)
}
}