First pass on adding remote backups

+ Added remote_backup.rs to handle remote backup logic
+ Added `remote` section to the config for optional remote backup setup
+ Using ssh2 rust library for SFTP support
+ small tweaks
This commit is contained in:
zerohd committed 2021-02-02 21:21:55 -06:00
1 parent a7e2b260bc
commit d94186c7b8
6 files changed
+193 -21

No files matched your search

+36 -20
View File
@@ -2,6 +2,7 @@ use crate::backup;
use crate::config::{AlbatrossConfig, WorldConfig, WorldType};
use crate::discord::send_webhook;
use crate::region::Region;
use crate::remote_backup::remote_backup;
use chrono::{NaiveDateTime, Utc};
use flate2::read::GzDecoder;
use flate2::write::GzEncoder;
@@ -321,8 +322,42 @@ pub fn do_backup(cfg: AlbatrossConfig, output: Option<PathBuf>) -> Result<(), st
create_dir_all(tmp_dir.clone()).unwrap();
send_webhook("**Albatross is swooping in to backup your worlds!**", &cfg);
let timer = Instant::now();
backup_worlds(&cfg, server_base_dir, worlds, &mut tmp_dir)?;
backup::compress_backup(&tmp_dir, &output_archive)?;
remove_dir_all(&tmp_dir)?;
let backups_removed = remove_old_backups(&cfg.backup.output_dir, cfg.backup.backups_to_keep)?;
if backups_removed > 0 {
let msg = format!(
"Albatross mistook **{}** of your old backups for some french fries and ate them!! SKRAWWWW",
backups_removed
);
send_webhook(msg.as_str(), &cfg);
}
if let Some(remote_backup_cfg) = &cfg.remote {
remote_backup(output_archive, remote_backup_cfg);
}
let secs = timer.elapsed().as_secs();
send_webhook(
format!("**Full backup completed in {}s**! *SKREEEEEEEEEE*", secs).as_str(),
&cfg,
);
Ok(())
}
fn backup_worlds(
cfg: &AlbatrossConfig,
server_base_dir: PathBuf,
worlds: Vec<WorldConfig>,
tmp_dir: &mut PathBuf,
) -> Result<(), std::io::Error> {
send_webhook("**Albatross is swooping in to backup your worlds!**", &cfg);
for world in worlds {
let mut world_dir = server_base_dir.clone();
let world_name = world.world_name.clone();
@@ -363,24 +398,5 @@ pub fn do_backup(cfg: AlbatrossConfig, output: Option<PathBuf>) -> Result<(), st
}
}
backup::compress_backup(&tmp_dir, &output_archive)?;
remove_dir_all(&tmp_dir)?;
let backups_removed = remove_old_backups(&cfg.backup.output_dir, cfg.backup.backups_to_keep)?;
if backups_removed > 0 {
let msg = format!(
"Albatross mistook **{}** of your old backups for some french fries and ate them!! SKRAWWWW",
backups_removed
);
send_webhook(msg.as_str(), &cfg);
}
let secs = timer.elapsed().as_secs();
send_webhook(
format!("**Full backup completed in {}s**! *SKREEEEEEEEEE*", secs).as_str(),
&cfg,
);
Ok(())
}
+21 -1
View File
@@ -24,7 +24,7 @@ impl From<String> for WorldType {
}
}
/// Config for individual WorldConfig
/// Config for individual world configuration
#[derive(Debug, Deserialize, Clone)]
pub struct WorldConfig {
pub world_name: String,
@@ -41,11 +41,31 @@ pub struct BackupConfig {
pub discord_webhook: Option<String>,
}
/// Config for remote backups
#[derive(Debug, Deserialize, Clone)]
pub struct RemoteBackupConfig {
/// Remote server address
pub sftp_server_addr: String,
/// Remote output directory
pub remote_dir: PathBuf,
/// Remote server username
pub username: String,
/// Public key for key auth
pub public_key: Option<PathBuf>,
/// Private key for key auth
pub private_key: Option<PathBuf>,
/// Password if using password auth
pub password: Option<String>,
/// Remote backups to keep
pub backups_to_keep: u64,
}
/// Configs
#[derive(Debug, Deserialize, Clone)]
pub struct AlbatrossConfig {
pub backup: BackupConfig,
pub world_config: Option<Vec<WorldConfig>>,
pub remote: Option<RemoteBackupConfig>,
}
impl AlbatrossConfig {
+1
View File
@@ -6,6 +6,7 @@ mod chunk_coordinate;
mod config;
mod discord;
mod region;
mod remote_backup;
mod restore;
use crate::backup::{convert_backup_to_sp, do_backup};
+47
View File
@@ -0,0 +1,47 @@
use crate::config::RemoteBackupConfig;
use ssh2::Session;
use std::net::TcpStream;
use std::path::PathBuf;
fn open_ssh_session(remote_config: &RemoteBackupConfig) -> Session {
let tcp = TcpStream::connect(&remote_config.sftp_server_addr).unwrap();
let mut sess = Session::new().unwrap();
sess.set_tcp_stream(tcp);
sess.handshake().unwrap();
if let Some(password) = &remote_config.password {
sess.userauth_password(&remote_config.username, password)
.unwrap();
} else if let Some(key) = &remote_config.private_key {
let public_key =
remote_config
.public_key
.as_ref()
.map(|pub_key| pub_key.as_path().clone());
sess.userauth_pubkey_file(
&remote_config.username,
public_key,
key,
None,
)
.unwrap();
} else {
panic!("No key provided")
}
sess
}
pub fn remote_backup(file: PathBuf, remote_config: &RemoteBackupConfig) {
let sess = open_ssh_session(remote_config);
let remote_path = remote_config.remote_dir.join(file.file_name().unwrap());
let mut local_file = std::fs::File::open(&file).unwrap();
let sftp = sess.sftp().unwrap();
let mut remote_file = sftp.create(&remote_path).unwrap();
std::io::copy(&mut local_file, &mut remote_file).unwrap();
}