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

View File

@ -12,13 +12,13 @@ use std::fs::File;
use std::io; use std::io;
use std::path::PathBuf; use std::path::PathBuf;
/// Manages a single backup layer consisting of one or more chains of backups.
pub struct Manager<T> pub struct Manager<T>
where where
T: Clone + Serialize + for<'de> Deserialize<'de>, T: Clone + Serialize + for<'de> Deserialize<'de>,
{ {
backup_dir: PathBuf, backup_dir: PathBuf,
config_dir: PathBuf, dirs: Vec<(PathBuf, PathBuf)>,
world_dir: PathBuf,
default_metadata: T, default_metadata: T,
chain_len: u64, chain_len: u64,
chains_to_keep: u64, chains_to_keep: u64,
@ -32,10 +32,9 @@ where
{ {
const METADATA_FILE: &str = "alex.json"; const METADATA_FILE: &str = "alex.json";
pub fn new<P1: Into<PathBuf>, P2: Into<PathBuf>, P3: Into<PathBuf>>( pub fn new<P: Into<PathBuf>>(
backup_dir: P1, backup_dir: P,
config_dir: P2, dirs: Vec<(PathBuf, PathBuf)>,
world_dir: P3,
metadata: T, metadata: T,
chain_len: u64, chain_len: u64,
chains_to_keep: u64, chains_to_keep: u64,
@ -43,8 +42,7 @@ where
) -> Self { ) -> Self {
Self { Self {
backup_dir: backup_dir.into(), backup_dir: backup_dir.into(),
config_dir: config_dir.into(), dirs,
world_dir: world_dir.into(),
default_metadata: metadata, default_metadata: metadata,
chain_len, chain_len,
chains_to_keep, chains_to_keep,
@ -55,11 +53,6 @@ where
/// Create a new backup with the expected type. /// Create a new backup with the expected type.
pub fn create_backup(&mut self) -> io::Result<()> { 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 // We start a new chain if the current chain is complete, or if there isn't a first chain
// yet // yet
if let Some(current_chain) = self.chains.last() { if let Some(current_chain) = self.chains.last() {
@ -78,9 +71,14 @@ where
let previous_backup = current_chain.last().unwrap(); let previous_backup = current_chain.last().unwrap();
let state = Backup::state(current_chain); 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 { } else {
Backup::create(&self.backup_dir, dirs)? Backup::create(&self.backup_dir, &self.dirs)?
}; };
backup.set_metadata(self.default_metadata.clone()); 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. /// The `Backup` instance describing this new backup.
pub fn create<P: AsRef<Path>>( pub fn create<P: AsRef<Path>>(
backup_dir: P, backup_dir: P,
dirs: Vec<(PathBuf, PathBuf)>, dirs: &Vec<(PathBuf, PathBuf)>,
) -> io::Result<Self> { ) -> io::Result<Self> {
let start_time = chrono::offset::Utc::now(); 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() { for entry in src_dir.read_dir_recursive()?.ignored("cache").files() {
let path = entry?.path(); 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))?; ar.append_path_with_name(&path, dir_in_tar.join(stripped))?;
added_files.insert(stripped.to_path_buf()); 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 { Ok(Backup {
@ -115,7 +115,7 @@ impl<T: Clone> Backup<T> {
previous_state: HashMap<PathBuf, HashSet<PathBuf>>, previous_state: HashMap<PathBuf, HashSet<PathBuf>>,
previous_start_time: chrono::DateTime<Utc>, previous_start_time: chrono::DateTime<Utc>,
backup_dir: P, backup_dir: P,
dirs: Vec<(PathBuf, PathBuf)>, dirs: &Vec<(PathBuf, PathBuf)>,
) -> io::Result<Self> { ) -> io::Result<Self> {
let start_time = chrono::offset::Utc::now(); 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() { for entry in src_dir.read_dir_recursive()?.ignored("cache").files() {
let path = entry?.path(); 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) { if !path.not_modified_since(previous_start_time) {
ar.append_path_with_name(&path, dir_in_tar.join(stripped))?; 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); 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( delta.removed.insert(
dir_in_tar, dir_in_tar.to_path_buf(),
previous_files.difference(&all_files).cloned().collect(), previous_files.difference(&all_files).cloned().collect(),
); );
} }

View File

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