diff --git a/docker-compose.yml b/docker-compose.yml index 5def340..392afb8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,6 +17,7 @@ services: LOG: debug TZ: "Europe/Paris" EDGE_KEY: "eyJzZXJ2ZXJVcmwiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODciLCJhZ2VudElkIjoiOTI0OGU1ZWUtYWU5Yi00ZGQ0LTk3MDUtYjEwYWQzNDU4YmNjIiwicHVibGljS2V5IjoiLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tXG5NSUlCQ2dLQ0FRRUE5TWV4M2pmdnVLdFB5YU1ERnh2Ulp2dmd3YkRJQ2JzQi81Wll5NDNSVVRBaXZRYjJiSDdYXG5qRHBQd1lJeCs4UFBrbHlRbDVMQzV1UWZEaCs4SVd4OG1LZ3FvMXpWMkdiZXdGbEdEWFYxVEdyU1ZEU25aSWR4XG52bWdYc29EeXhVMlJvWUFUMS9YMWxuc2YxenZKdkFMTkhXdEhRdk42SjVDZTFSMmFsendVRGFEVXlJNzRmSldQXG5tNTh0SDMrYklXL0VVTXdjaWNxM0oySWw3Vm9KNkZNUHJQL1ZSOWEvdFF1SU1qa200MXpFY2NscExPa2luRkxuXG54NmVUWkFSZUpya2UrbnRvZ2t4TGEyRWV5a1lUNzB4V3hKNWp5ZExBVnRvNkkyQlVLVVJoTkowTUFaU29NYUtvXG5iMGJRcnY1UzExZWllMnMrT2I3aTYzSFpkVUx0UmV1MVJ3SURBUUFCXG4tLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tXG4ifQ" +# EDGE_KEY: "eyJzZXJ2ZXJVcmwiOiJodHRwOi8vbG9jYWxob3N0Ojg4ODciLCJhZ2VudElkIjoiZWE2NTg1MDctZTA5My00NDUxLWIxZDAtMDgwZWZjMGNmNWYzIiwicHVibGljS2V5IjoiLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tXG5NSUlCQ2dLQ0FRRUE5TWV4M2pmdnVLdFB5YU1ERnh2Ulp2dmd3YkRJQ2JzQi81Wll5NDNSVVRBaXZRYjJiSDdYXG5qRHBQd1lJeCs4UFBrbHlRbDVMQzV1UWZEaCs4SVd4OG1LZ3FvMXpWMkdiZXdGbEdEWFYxVEdyU1ZEU25aSWR4XG52bWdYc29EeXhVMlJvWUFUMS9YMWxuc2YxenZKdkFMTkhXdEhRdk42SjVDZTFSMmFsendVRGFEVXlJNzRmSldQXG5tNTh0SDMrYklXL0VVTXdjaWNxM0oySWw3Vm9KNkZNUHJQL1ZSOWEvdFF1SU1qa200MXpFY2NscExPa2luRkxuXG54NmVUWkFSZUpya2UrbnRvZ2t4TGEyRWV5a1lUNzB4V3hKNWp5ZExBVnRvNkkyQlVLVVJoTkowTUFaU29NYUtvXG5iMGJRcnY1UzExZWllMnMrT2I3aTYzSFpkVUx0UmV1MVJ3SURBUUFCXG4tLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tXG4ifQ" #POOLING: 1 #DATABASES_CONFIG_FILE: "config.toml" extra_hosts: diff --git a/scripts/README.md b/scripts/README.md index b931fa8..4160bc3 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -1,11 +1,12 @@ # Seed instructions -## MongoDB +## MongoDB ```bash make seed-mongo make seed-mongo-auth make seed-mysql +make seed-mysql-1gb make seed-postgres make seed-postgres-1gb make seed-all diff --git a/src/services/backup.rs b/src/services/backup.rs index f0d0f1b..f39f3d5 100644 --- a/src/services/backup.rs +++ b/src/services/backup.rs @@ -13,6 +13,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use tempfile::TempDir; use tracing::{error, info}; +use crate::utils::locks::{DbOpLock, FileLock}; #[derive(Debug, Clone)] pub struct BackupResult { @@ -62,83 +63,92 @@ impl BackupService { tokio::spawn(async move { match TempDir::new() { Ok(temp_dir) => { - match ctx - .api - .backup_create( - method.clone().to_string(), - ctx.edge_key.agent_id.clone(), - &generated_id_clone, - ) - .await - { - Ok(backup_created_result) => { - info!("Backup created successfully"); + match FileLock::is_locked(&generated_id_clone).await { + Ok(true) => { + error!("Backup already running for {}", &generated_id_clone); + return; + } + Ok(false) => { - let tmp_path = temp_dir.path().to_path_buf(); - info!("Created temp directory {}", tmp_path.display()); - match BackupService::run(db_cfg, &tmp_path).await { - Ok(mut result) => { - if let Some(backup_file) = result.backup_file.take() { - match compress_to_tar_gz_large(&backup_file).await { - Ok(compression_result) => { - result.backup_file = - Some(compression_result.compressed_path); - let service = BackupService { ctx: ctx.clone() }; - let backup_id = backup_created_result.unwrap().backup.id; - match service - .upload( - result.clone(), - method, - storages_clone.clone(), - encrypt, - &backup_id, - ) - .await - { - Ok(upload_result) => { + match ctx + .api + .backup_create( + method.clone().to_string(), + ctx.edge_key.agent_id.clone(), + &generated_id_clone, + ) + .await + { + Ok(backup_created_result) => { + info!("Backup created successfully"); + let tmp_path = temp_dir.path().to_path_buf(); + info!("Created temp directory {}", tmp_path.display()); + match BackupService::run(db_cfg, &tmp_path).await { + Ok(mut result) => { + if let Some(backup_file) = result.backup_file.take() { + match compress_to_tar_gz_large(&backup_file).await { + Ok(compression_result) => { + result.backup_file = + Some(compression_result.compressed_path); + let service = BackupService { ctx: ctx.clone() }; + let backup_id = backup_created_result.unwrap().backup.id; match service - .send_result( - result, - upload_result, - &backup_id + .upload( + result.clone(), + method, + storages_clone.clone(), + encrypt, + &backup_id, ) .await { - Ok(_) => { - return; - } - Err(e) => { - error!( + Ok(upload_result) => { + match service + .send_result( + result, + upload_result, + &backup_id + ) + .await + { + Ok(_) => { + return; + } + Err(e) => { + error!( "Failed to send backup result: {}", e ); + } + } + } + Err(e) => { + error!( + "Failed to upload backup files: {}", + e + ); } } } Err(e) => { error!( - "Failed to upload backup files: {}", - e - ); - } - } - } - Err(e) => { - error!( "Failed to compress backup file : {}", e ); + } + } + } else { + error!("No backup file generated"); } } - } else { - error!("No backup file generated"); + Err(e) => error!("BackupService run failed: {}", e), } + // TempDir is automatically deleted when dropped here } - Err(e) => error!("BackupService run failed: {}", e), + Err(e) => error!("Backup creation failed: {}", e), } - // TempDir is automatically deleted when dropped here - } - Err(e) => error!("Backup creation failed: {}", e), + }, + Err(e) => error!("An error occurred while checking lock : {}", e), } } Err(e) => error!("Failed to create temp dir: {}", e), @@ -251,13 +261,31 @@ impl BackupService { info!("Storage {} uploaded to remote path {:?}", storage_id, upload_result.remote_file_path); + let (remote_path, total_size) = match ( + &upload_result.remote_file_path, + upload_result.total_size, + ) { + (Some(path), Some(size)) => (path.clone(), size), + _ => { + return UploadResult { + storage_id: storage_id.clone(), + success: false, + error: Some("remote_file_path or total_size missing".to_string()), + remote_file_path: None, + total_size: None, + } + } + }; + + + match self.ctx.api.backup_upload_status( self.ctx.edge_key.agent_id.clone(), generated_id.clone(), backup_storage_id, status, - upload_result.remote_file_path.clone().unwrap(), - upload_result.total_size.clone().unwrap(), + remote_path, + total_size, backup_id ).await { Ok(_) => { diff --git a/src/utils/file.rs b/src/utils/file.rs index 4bee3b7..c447b6e 100644 --- a/src/utils/file.rs +++ b/src/utils/file.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + use anyhow::Result; use async_stream::try_stream; use bytes::Bytes; diff --git a/src/utils/locks.rs b/src/utils/locks.rs index 76b1d74..7e3d0dc 100644 --- a/src/utils/locks.rs +++ b/src/utils/locks.rs @@ -1,10 +1,10 @@ use anyhow::{Context, Result}; -use chrono::{Local}; -use tracing::{info, warn, error}; +use chrono::Local; use std::path::{Path, PathBuf}; use std::time::{Duration, SystemTime}; -use tokio::fs::{OpenOptions, metadata, remove_file, create_dir_all, read_dir}; +use tokio::fs::{OpenOptions, create_dir_all, metadata, read_dir, remove_file}; use tokio::io::AsyncWriteExt; +use tracing::{error, info, warn}; /// Lock type for logging purposes #[derive(Debug, Copy, Clone)] @@ -59,6 +59,24 @@ impl FileLock { Ok(()) } + pub async fn is_locked(id: &str) -> Result { + let path = Self::lock_file_path(id); + + if !path.exists() { + return Ok(false); + } + + let meta = metadata(&path).await?; + + if let Ok(modified) = meta.modified() { + let age = SystemTime::now().duration_since(modified)?; + + if age > Duration::from_secs(24 * 60 * 60) { + return Ok(false); + } + } + Ok(true) + } /// Acquire a file-based lock pub async fn acquire(id: &str, service_name: &str) -> Result<()> { @@ -88,10 +106,13 @@ impl FileLock { .await .with_context(|| format!("Failed to create lock file for {}", id))?; - f.write_all(format!("Service: {}\n", service_name).as_bytes()).await?; - f.write_all(format!("PID: {}\n", std::process::id()).as_bytes()).await?; + f.write_all(format!("Service: {}\n", service_name).as_bytes()) + .await?; + f.write_all(format!("PID: {}\n", std::process::id()).as_bytes()) + .await?; // f.write_all(format!("Timestamp: {}\n", Utc::now()).as_bytes()).await?; - f.write_all(format!("Timestamp: {}\n", Local::now()).as_bytes()).await?; + f.write_all(format!("Timestamp: {}\n", Local::now()).as_bytes()) + .await?; info!("Successfully acquired lock for {}", id); Ok(()) @@ -106,7 +127,10 @@ impl FileLock { remove_file(&path).await?; info!("Released file lock for {}", id); } else { - warn!("Attempted to release lock for {}, but file does not exist", id); + warn!( + "Attempted to release lock for {}, but file does not exist", + id + ); } Ok(()) }