From 8378f2ead364ac930039b2f64ae940fb1133cde7 Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Sun, 19 Sep 2021 13:56:30 -0600 Subject: [PATCH] Added File remote backups + Copies the backup to a target directory + Useful for backing up to another local drive or mounted network drive + Various improvements + Fixed .drone.yml --- .drone.yml | 6 +++--- Cargo.lock | 4 +++- Cargo.toml | 2 +- README.md | 14 ++++++++------ src/backup.rs | 27 ++++++++++++++++----------- src/config/mod.rs | 13 +++++++------ src/config/remote.rs | 7 +++++++ src/error.rs | 2 -- src/remote/file.rs | 8 ++++---- src/remote/ftp.rs | 18 ++++++------------ src/remote/mod.rs | 10 +++++----- src/remote/sftp.rs | 25 ++++++++++--------------- 12 files changed, 70 insertions(+), 66 deletions(-) diff --git a/.drone.yml b/.drone.yml index b19916a..c6494fd 100644 --- a/.drone.yml +++ b/.drone.yml @@ -10,7 +10,7 @@ trigger: steps: - name: build pull: always - image: rust:1.46.0 + image: rust:1.55.0 commands: - cargo build --verbose @@ -28,7 +28,7 @@ trigger: steps: - name: build pull: always - image: rust:1.46.0 + image: rust:1.55.0 commands: - cargo build --verbose --release - name: gitea-release @@ -37,6 +37,6 @@ steps: settings: token: from_secret: gitea_token - base: https://git.etztech.xyz + base: https://git.canopymc.net files: - "target/release/albatross" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 5a7ad6c..43938b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "addr2line" version = "0.13.0" @@ -41,7 +43,7 @@ dependencies = [ [[package]] name = "albatross" -version = "0.4.0" +version = "0.5.0" dependencies = [ "anvil-region", "chrono 0.4.19", diff --git a/Cargo.toml b/Cargo.toml index b3cd9ee..2f9b442 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "albatross" -version = "0.4.0" +version = "0.5.0" authors = ["Joey Hines "] edition = "2018" diff --git a/README.md b/README.md index 07ef4bc..250910e 100644 --- a/README.md +++ b/README.md @@ -54,14 +54,16 @@ Restoring a range of chunks (from -2,-2 to 2,2): [backup] # Minecraft sever directory minecraft_dir = "/home/mc/server" -# Directory to place backups -output_dir = "/home/mc/backups" -# Number of backups to keep -backups_to_keep = 10 # Optional Discord webhook discord_webhook = "https://discordapp.com/api/webhooks/" +# Number of backups to keep +backups_to_keep = 10 -# Optional remote backup config +[backup.output_config] +# Directory to place backups +path = "/home/mc/backups" + +# Optional remote_backup backup config [remote] # SFTP server host:port sftp_server_addr = "localhost:22" @@ -74,7 +76,7 @@ password = "cooluser123" # Key Auth #public_key = /home/user/.ssh/id_rsa.pub" #private_key = /home/user/.ssh/id_rsa" -# Backups to keep on the remote host +# Backups to keep on the remote_backup host backups_to_keep = 3 # World config options diff --git a/src/backup.rs b/src/backup.rs index 32a41d9..b3b80cb 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -137,11 +137,11 @@ pub fn backup_overworld( world_config: &WorldConfig, ) -> Result<(u64, u64)> { backup_dir("data", &world_path, &backup_path)?; - backup_dir("stats", &world_path, &backup_path)?; + backup_dir("stats", &world_path, &backup_path).ok(); backup_file("level.dat", world_path.clone(), backup_path.clone())?; - backup_file("level.dat_old", world_path.clone(), backup_path.clone())?; - backup_file("session.lock", world_path.clone(), backup_path.clone())?; + backup_file("level.dat_old", world_path.clone(), backup_path.clone()).ok(); + backup_file("session.lock", world_path.clone(), backup_path.clone()).ok(); backup_file("uid.dat", world_path.clone(), backup_path.clone())?; let player_count = backup_dir("playerdata", &world_path, &backup_path)?; @@ -249,20 +249,25 @@ pub fn convert_backup_to_sp( Ok(()) } -/// Preform a remote backup, if configured +/// Preform a remote_backup backup, if configured pub fn do_remote_backup( remote_backup_cfg: &RemoteBackupConfig, backup_path: PathBuf, ) -> Result<()> { - if remote_backup_cfg.sftp.is_some() { - let mut sftp_backup = SFTPBackup::new(&remote_backup_cfg)?; + if let Some(config) = &remote_backup_cfg.sftp { + let mut sftp_backup = SFTPBackup::new(config, remote_backup_cfg.backups_to_keep)?; sftp_backup.backup_to_remote(backup_path)?; sftp_backup.cleanup()?; - } else if remote_backup_cfg.ftp.is_some() { - let mut ftps_backup = FTPBackup::new(&remote_backup_cfg)?; + } else if let Some(config) = &remote_backup_cfg.ftp { + let mut ftps_backup = FTPBackup::new(config, remote_backup_cfg.backups_to_keep)?; ftps_backup.backup_to_remote(backup_path)?; ftps_backup.cleanup()?; } + else if let Some(config) = &remote_backup_cfg.file { + let mut file_backup = FileBackup::new(config, remote_backup_cfg.backups_to_keep)?; + file_backup.backup_to_remote(backup_path)?; + file_backup.cleanup()?; + } Ok(()) } @@ -278,10 +283,10 @@ pub fn do_backup(cfg: AlbatrossConfig, output: Option) -> Result<()> { let backup_name = format!("{}_backup.tar.gz", time_str); let mut output_archive = match output { Some(out_path) => out_path, - None => cfg.backup.output_dir.clone(), + None => cfg.backup.output_config.path.clone(), }; output_archive.push(backup_name); - let mut tmp_dir = cfg.backup.output_dir.clone(); + let mut tmp_dir = cfg.backup.output_config.path.clone(); tmp_dir.push("tmp"); remove_dir_all(&tmp_dir).ok(); @@ -305,7 +310,7 @@ pub fn do_backup(cfg: AlbatrossConfig, output: Option) -> Result<()> { remove_dir_all(&tmp_dir)?; - let mut local_backup = FileBackup::new(&cfg.backup).unwrap(); + let mut local_backup = FileBackup::new(&cfg.backup.output_config, cfg.backup.backups_to_keep).unwrap(); match local_backup.cleanup() { Ok(backups_removed) => { diff --git a/src/config/mod.rs b/src/config/mod.rs index 84644d2..2aec30f 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,6 +1,6 @@ -mod remote; +pub(crate) mod remote; -use crate::config::remote::{FTPConfig, SFTPConfig}; +use crate::config::remote::{FTPConfig, SFTPConfig, FileConfig}; use config::{Config, ConfigError, File}; use serde::Deserialize; use std::path::PathBuf; @@ -39,17 +39,18 @@ pub struct WorldConfig { #[derive(Debug, Deserialize, Clone)] pub struct BackupConfig { pub minecraft_dir: PathBuf, - pub output_dir: PathBuf, - pub backups_to_keep: u64, + pub backups_to_keep: usize, pub discord_webhook: Option, + pub output_config: FileConfig, } -/// Config for remote backups +/// Config for remote_backup backups #[derive(Debug, Deserialize, Clone)] pub struct RemoteBackupConfig { - pub backups_to_keep: u64, + pub backups_to_keep: usize, pub sftp: Option, pub ftp: Option, + pub file: Option } /// Configs diff --git a/src/config/remote.rs b/src/config/remote.rs index bd7be52..2158f14 100644 --- a/src/config/remote.rs +++ b/src/config/remote.rs @@ -30,3 +30,10 @@ pub struct FTPConfig { /// Password pub password: String, } + +/// File Config +#[derive(Debug, Deserialize, Clone)] +pub struct FileConfig { + /// Path to backup to + pub path: PathBuf, +} diff --git a/src/error.rs b/src/error.rs index be3150a..fd41427 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,7 +10,6 @@ pub enum AlbatrossError { RegionParseError(crate::region::RegionParseError), ChronoParseError(chrono::ParseError), NoSSHAuth, - RemoteConfigError(String), FTPError(ftp::FtpError), } @@ -27,7 +26,6 @@ impl std::fmt::Display for AlbatrossError { AlbatrossError::RegionParseError(e) => write!(f, "Unable to parse region name: {}", e), AlbatrossError::ChronoParseError(e) => write!(f, "Unable to parse time: {}", e), AlbatrossError::NoSSHAuth => write!(f, "No SSH auth methods provided in the config"), - AlbatrossError::RemoteConfigError(e) => write!(f, "Invalid configuration for {}", e), AlbatrossError::FTPError(e) => write!(f, "FTP error: {}", e), } } diff --git a/src/remote/file.rs b/src/remote/file.rs index 7348073..84f0c4c 100644 --- a/src/remote/file.rs +++ b/src/remote/file.rs @@ -1,4 +1,4 @@ -use crate::config::BackupConfig; +use crate::config::remote::FileConfig; use crate::error::Result; use crate::remote::{PathLocation, RemoteBackupSite}; use std::path::PathBuf; @@ -12,10 +12,10 @@ pub struct FileBackup { impl FileBackup { /// New FileBackup - pub fn new(config: &BackupConfig) -> Result { + pub fn new(config: &FileConfig, backups_to_keep: usize) -> Result { Ok(Self { - target_dir: config.output_dir.clone(), - backups_to_keep: config.backups_to_keep as usize, + target_dir: config.path.clone(), + backups_to_keep, }) } } diff --git a/src/remote/ftp.rs b/src/remote/ftp.rs index 75d9d8b..451db90 100644 --- a/src/remote/ftp.rs +++ b/src/remote/ftp.rs @@ -1,10 +1,9 @@ use ftp::FtpStream; use std::path::PathBuf; -use crate::config::RemoteBackupConfig; use crate::error; -use crate::error::AlbatrossError; use crate::remote::{PathLocation, RemoteBackupSite}; +use crate::config::remote::FTPConfig; /// FTP Remote Site pub struct FTPBackup { @@ -18,20 +17,15 @@ pub struct FTPBackup { impl FTPBackup { /// New FTPBackup - pub fn new(config: &RemoteBackupConfig) -> error::Result { - let ftp_config = config - .ftp - .as_ref() - .ok_or_else(|| AlbatrossError::RemoteConfigError("FTP".to_string()))?; + pub fn new(config: &FTPConfig, backups_to_keep: usize) -> error::Result { + let mut ftp_stream = FtpStream::connect(&config.server_addr)?; - let mut ftp_stream = FtpStream::connect(&ftp_config.server_addr)?; - - ftp_stream.login(&ftp_config.username, &ftp_config.password)?; + ftp_stream.login(&config.username, &config.password)?; Ok(Self { stream: ftp_stream, - target_dir: ftp_config.remote_dir.clone(), - backups_to_keep: config.backups_to_keep as usize, + target_dir: config.remote_dir.clone(), + backups_to_keep, }) } } diff --git a/src/remote/mod.rs b/src/remote/mod.rs index e61605f..eae7154 100644 --- a/src/remote/mod.rs +++ b/src/remote/mod.rs @@ -9,13 +9,13 @@ pub mod ftp; pub mod sftp; pub trait RemoteBackupFile { - /// Type containing the location of the remote backup + /// Type containing the location of the remote_backup backup type LocationType; /// Get the underlying location type fn location(&self) -> Self::LocationType; - /// Get the time the remote file was created + /// Get the time the remote_backup file was created fn time_created(&self) -> chrono::NaiveDateTime; /// Parse the time created from the file name @@ -34,10 +34,10 @@ pub trait RemoteBackupSite { /// Struct representing the location of a backup on the site type FileType: RemoteBackupFile; - /// Backup a file to the the remote site + /// Backup a file to the the remote_backup site fn backup_to_remote(&mut self, file: PathBuf) -> Result<()>; - /// Get the locations backups contained on the remote site + /// Get the locations backups contained on the remote_backup site fn get_backups(&mut self) -> Result>; /// Remove a backup from the side @@ -46,7 +46,7 @@ pub trait RemoteBackupSite { /// Number of backups to keep on the site fn backups_to_keep(&self) -> usize; - /// Cleanup old backups on the remote site + /// Cleanup old backups on the remote_backup site fn cleanup(&mut self) -> Result { let mut backups = self.get_backups()?; diff --git a/src/remote/sftp.rs b/src/remote/sftp.rs index 6b6b116..88ade27 100644 --- a/src/remote/sftp.rs +++ b/src/remote/sftp.rs @@ -3,10 +3,10 @@ use std::path::PathBuf; use ssh2::Session; -use crate::config::RemoteBackupConfig; use crate::error; use crate::error::AlbatrossError; use crate::remote::{PathLocation, RemoteBackupSite}; +use crate::config::remote::SFTPConfig; /// SFTP Remote Site pub struct SFTPBackup { @@ -20,30 +20,25 @@ pub struct SFTPBackup { impl SFTPBackup { /// New SFTPBackup - pub fn new(config: &RemoteBackupConfig) -> error::Result { - let sftp_config = config - .sftp - .as_ref() - .ok_or_else(|| AlbatrossError::RemoteConfigError("SFTP".to_string()))?; - - let tcp = TcpStream::connect(&sftp_config.server_addr)?; + pub fn new(config: &SFTPConfig, backups_to_keep: usize) -> error::Result { + let tcp = TcpStream::connect(&config.server_addr)?; let mut sess = Session::new()?; sess.set_tcp_stream(tcp); sess.handshake().unwrap(); - if let Some(password) = &sftp_config.password { - sess.userauth_password(&sftp_config.username, password)?; - } else if let Some(key) = &sftp_config.private_key { - let public_key = sftp_config.public_key.as_deref(); - sess.userauth_pubkey_file(&sftp_config.username, public_key, key, None)?; + if let Some(password) = &config.password { + sess.userauth_password(&config.username, password)?; + } else if let Some(key) = &config.private_key { + let public_key = config.public_key.as_deref(); + sess.userauth_pubkey_file(&config.username, public_key, key, None)?; } else { return Err(AlbatrossError::NoSSHAuth); } Ok(Self { session: sess, - target_dir: sftp_config.remote_dir.clone(), - backups_to_keep: config.backups_to_keep as usize, + target_dir: config.remote_dir.clone(), + backups_to_keep, }) } } -- 2.41.0