feat: further generalize backup code

export-backup
Jef Roosens 2023-06-23 14:36:20 +02:00
parent 03e21fda87
commit d5cea49c8b
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
4 changed files with 39 additions and 38 deletions

View File

@ -6,13 +6,13 @@ use std::collections::HashMap;
use std::io;
use std::path::PathBuf;
/// Manages a collection of backup layers, allowing them to be utilized as a single object.
pub struct MetaManager<T>
where
T: Clone + Serialize + for<'de> Deserialize<'de>,
{
backup_dir: PathBuf,
config_dir: PathBuf,
world_dir: PathBuf,
dirs: Vec<(PathBuf, PathBuf)>,
default_metadata: T,
managers: HashMap<String, Manager<T>>,
}
@ -21,21 +21,20 @@ impl<T> MetaManager<T>
where
T: Clone + Serialize + for<'de> Deserialize<'de>,
{
pub fn new<P1: Into<PathBuf>, P2: Into<PathBuf>, P3: Into<PathBuf>>(
backup_dir: P1,
config_dir: P2,
world_dir: P3,
pub fn new<P: Into<PathBuf>>(
backup_dir: P,
dirs: Vec<(PathBuf, PathBuf)>,
default_metadata: T,
) -> Self {
MetaManager {
backup_dir: backup_dir.into(),
config_dir: config_dir.into(),
world_dir: world_dir.into(),
dirs,
default_metadata,
managers: HashMap::new(),
}
}
/// Add a new manager to track, initializing it first.
pub fn add(&mut self, config: &ManagerConfig) -> io::Result<()> {
// Backup dir itself should exist, but we control its contents, so we can create
// separate directories for each layer
@ -52,8 +51,7 @@ where
let mut manager = Manager::new(
path,
self.config_dir.clone(),
self.world_dir.clone(),
self.dirs.clone(),
self.default_metadata.clone(),
config.chain_len,
config.chains,
@ -65,6 +63,7 @@ where
Ok(())
}
/// Convenient wrapper for `add`.
pub fn add_all(&mut self, configs: &Vec<ManagerConfig>) -> io::Result<()> {
for config in configs {
self.add(config)?;
@ -73,6 +72,7 @@ where
Ok(())
}
/// Return the name of the next scheduled layer, if one or more managers are present.
pub fn next_scheduled_layer(&self) -> Option<&str> {
self.managers
.iter()
@ -80,6 +80,7 @@ where
.map(|(k, _)| k.as_str())
}
/// Return the earliest scheduled time for the underlying managers.
pub fn next_scheduled_time(&self) -> Option<chrono::DateTime<Utc>> {
self.managers
.values()
@ -87,6 +88,7 @@ where
.min()
}
/// Perform a backup cycle for the earliest scheduled manager.
pub fn perform_backup_cycle(&mut self) -> io::Result<()> {
if let Some(manager) = self
.managers

View File

@ -12,13 +12,13 @@ use std::fs::File;
use std::io;
use std::path::PathBuf;
/// Manages a single backup layer consisting of one or more chains of backups.
pub struct Manager<T>
where
T: Clone + Serialize + for<'de> Deserialize<'de>,
{
backup_dir: PathBuf,
config_dir: PathBuf,
world_dir: PathBuf,
dirs: Vec<(PathBuf, PathBuf)>,
default_metadata: T,
chain_len: u64,
chains_to_keep: u64,
@ -32,10 +32,9 @@ where
{
const METADATA_FILE: &str = "alex.json";
pub fn new<P1: Into<PathBuf>, P2: Into<PathBuf>, P3: Into<PathBuf>>(
backup_dir: P1,
config_dir: P2,
world_dir: P3,
pub fn new<P: Into<PathBuf>>(
backup_dir: P,
dirs: Vec<(PathBuf, PathBuf)>,
metadata: T,
chain_len: u64,
chains_to_keep: u64,
@ -43,8 +42,7 @@ where
) -> Self {
Self {
backup_dir: backup_dir.into(),
config_dir: config_dir.into(),
world_dir: world_dir.into(),
dirs,
default_metadata: metadata,
chain_len,
chains_to_keep,
@ -55,11 +53,6 @@ where
/// Create a new backup with the expected type.
pub fn create_backup(&mut self) -> io::Result<()> {
let dirs = vec![
(PathBuf::from("config"), self.config_dir.clone()),
(PathBuf::from("worlds"), self.world_dir.clone()),
];
// We start a new chain if the current chain is complete, or if there isn't a first chain
// yet
if let Some(current_chain) = self.chains.last() {
@ -78,9 +71,14 @@ where
let previous_backup = current_chain.last().unwrap();
let state = Backup::state(current_chain);
Backup::create_from(state, previous_backup.start_time, &self.backup_dir, dirs)?
Backup::create_from(
state,
previous_backup.start_time,
&self.backup_dir,
&self.dirs,
)?
} else {
Backup::create(&self.backup_dir, dirs)?
Backup::create(&self.backup_dir, &self.dirs)?
};
backup.set_metadata(self.default_metadata.clone());

View File

@ -77,7 +77,7 @@ impl<T: Clone> Backup<T> {
/// The `Backup` instance describing this new backup.
pub fn create<P: AsRef<Path>>(
backup_dir: P,
dirs: Vec<(PathBuf, PathBuf)>,
dirs: &Vec<(PathBuf, PathBuf)>,
) -> io::Result<Self> {
let start_time = chrono::offset::Utc::now();
@ -93,13 +93,13 @@ impl<T: Clone> Backup<T> {
for entry in src_dir.read_dir_recursive()?.ignored("cache").files() {
let path = entry?.path();
let stripped = path.strip_prefix(&src_dir).unwrap();
let stripped = path.strip_prefix(src_dir).unwrap();
ar.append_path_with_name(&path, dir_in_tar.join(stripped))?;
added_files.insert(stripped.to_path_buf());
}
delta.added.insert(dir_in_tar, added_files);
delta.added.insert(dir_in_tar.to_path_buf(), added_files);
}
Ok(Backup {
@ -115,7 +115,7 @@ impl<T: Clone> Backup<T> {
previous_state: HashMap<PathBuf, HashSet<PathBuf>>,
previous_start_time: chrono::DateTime<Utc>,
backup_dir: P,
dirs: Vec<(PathBuf, PathBuf)>,
dirs: &Vec<(PathBuf, PathBuf)>,
) -> io::Result<Self> {
let start_time = chrono::offset::Utc::now();
@ -132,7 +132,7 @@ impl<T: Clone> Backup<T> {
for entry in src_dir.read_dir_recursive()?.ignored("cache").files() {
let path = entry?.path();
let stripped = path.strip_prefix(&src_dir).unwrap();
let stripped = path.strip_prefix(src_dir).unwrap();
if !path.not_modified_since(previous_start_time) {
ar.append_path_with_name(&path, dir_in_tar.join(stripped))?;
@ -144,9 +144,9 @@ impl<T: Clone> Backup<T> {
delta.added.insert(dir_in_tar.clone(), added_files);
if let Some(previous_files) = previous_state.get(&dir_in_tar) {
if let Some(previous_files) = previous_state.get(dir_in_tar) {
delta.removed.insert(
dir_in_tar,
dir_in_tar.to_path_buf(),
previous_files.difference(&all_files).cloned().collect(),
);
}

View File

@ -166,12 +166,13 @@ impl ServerCommand {
server_type: self.type_,
server_version: self.version.clone(),
};
let mut meta = MetaManager::new(
self.backup_dir.clone(),
self.config_dir.clone(),
self.world_dir.clone(),
metadata,
);
let dirs = vec![
(PathBuf::from("config"), self.config_dir.clone()),
(PathBuf::from("worlds"), self.world_dir.clone()),
];
let mut meta = MetaManager::new(self.backup_dir.clone(), dirs, metadata);
meta.add_all(&self.managers)?;
let mut cmd = self.create_cmd();