# Backup Module Multi-backend backup management library with support for restic, BorgBackup, TAR, CPIO, and rsync. ## Overview The backup module provides a unified interface for managing backups across different backend systems. It enables: - **Multi-backend support**: Use restic, borg, tar, cpio, or rsync for backups - **Configuration-driven**: TOML/YAML/JSON configuration with validation - **Dependency management**: Detect, verify, and guide installation of required tools - **Schedule automation**: Generate schedules for systemd, launchd, cron, and OpenRC - **Pattern filtering**: Include/exclude patterns with glob support for flexible file selection - **Retention policies**: Flexible backup retention with daily/weekly/monthly/yearly granularity - **Hooks**: Pre-backup, post-backup, and error handling hooks - **Notifications**: Configurable backup result notifications - **Standalone & Integrable**: Use as a library or integrate with Provctl/Provisioning ## Features by Backend | Feature | Restic | Borg | TAR | CPIO | Rsync | |---------|--------|------|-----|------|-------| | Incremental | ✓ | ✓ | ✗ | ✗ | ✓ | | Deduplication | ✓ | ✓ | ✗ | ✗ | ✗ | | Encryption | ✓ | ✓ | ✗ | ✗ | ✗ | | Remote Support | ✓ | ✓ | ✗ | ✗ | ✓ | | Compression | ✓ | ✓ | ✓ | ✓ | ✗ | ## Installation Add to `Cargo.toml`: ```toml [dependencies] backup = "0.1.0" ``` ## Quick Start ### Basic Configuration Create `backup.toml`: ```toml version = "1.0" name = "my-backup" description = "Daily backup of application data" backend = "restic" [repository] type = "local" path = "/mnt/backups/my-app" password_file = "/etc/backup/password" [[targets]] name = "app-data" paths = ["/var/lib/my-app", "/etc/my-app"] excludes = ["*.tmp", "*.log"] tags = ["daily", "production"] [retention] keep_daily = 7 keep_weekly = 4 keep_monthly = 12 [schedule] enabled = true cron = "0 2 * * *" # 2 AM daily init_system = "systemd" [notifications] enabled = true on_failure = true command = "mail -s 'Backup failed' admin@example.com" ``` ### Using the Library ```rust use backup::config::BackupConfig; use backup::backends::ResticBackend; use backup::backend::BackupBackend; #[tokio::main] async fn main() -> Result<(), Box> { // Load configuration let config = BackupConfig::from_toml("backup.toml")?; config.validate()?; // Create backend and perform backup let backend = ResticBackend::new(None); if !backend.is_available().await? { println!("Backend not available. Install it:"); let guide = backend.install_guide(); println!("{}", guide.description); return Ok(()); } // Initialize repository backend.init_repository(&config.repository).await?; // Perform backup let target = &config.targets[0]; let report = backend.backup(target).await?; println!("Backup complete: {} files, {} bytes", report.files_processed, report.size_processed); // Check repository let check = backend.check().await?; if !check.valid { println!("Repository has issues: {:?}", check.issues); } Ok(()) } ``` ## Configuration ### Repository Types - **local**: Local filesystem path - **s3**: Amazon S3 bucket - **sftp**: SFTP remote server - **rest**: REST backend server - **b2**: Backblaze B2 ### Backend Selection Choose the backend that best fits your needs: - **Restic**: Best for encrypted, incremental backups with deduplication - **BorgBackup**: Excellent for space-efficient deduplication and compression - **Rsync**: Good for fast incremental file synchronization - **TAR**: Simple, standard archive format for basic backups - **CPIO**: Alternative archive format, portable across systems ### Retention Policies Configure how long to keep backups: ```toml [retention] keep_last = 10 # Keep last 10 snapshots keep_hourly = 24 # Keep hourly snapshots for 24 hours keep_daily = 7 # Keep daily snapshots for 7 days keep_weekly = 4 # Keep weekly snapshots for 4 weeks keep_monthly = 12 # Keep monthly snapshots for 12 months keep_yearly = 5 # Keep yearly snapshots for 5 years keep_tags = ["important", "release"] # Never remove tagged snapshots ``` ### Scheduling Generate automated backup schedules: ```toml [schedule] enabled = true cron = "0 2 * * *" # 2 AM every day init_system = "systemd" # Use systemd timers on_failure = "notify" # Send notification on failure ``` Supported init systems: - **auto**: Auto-detect (systemd on Linux, launchd on macOS) - **systemd**: systemd timers (Linux) - **launchd**: LaunchAgent (macOS) - **cron**: Crontab scheduling - **openrc**: OpenRC init system ### Hooks Execute commands before/after backups: ```toml [hooks] pre_backup = [ "systemctl stop my-app", "pg_dump mydb > /tmp/db.sql" ] post_backup = [ "rm /tmp/db.sql", "systemctl start my-app" ] on_error = [ "mail -s 'Backup failed' admin@example.com" ] ``` ## API ### BackupBackend Trait All backends implement the `BackupBackend` async trait: ```rust pub trait BackupBackend: Send + Sync { fn name(&self) -> &'static str; async fn is_available(&self) -> BackupResult; async fn init_repository(&self, repo: &RepositoryConfig) -> BackupResult<()>; async fn backup(&self, target: &BackupTarget) -> BackupResult; async fn list_snapshots(&self) -> BackupResult>; async fn restore(&self, snapshot_id: &str, target_path: &Path, options: &RestoreOptions) -> BackupResult<()>; async fn prune(&self, policy: &RetentionPolicy) -> BackupResult; async fn check(&self) -> BackupResult; async fn stats(&self) -> BackupResult; } ``` ### Dependency Checking ```rust use backup::dependency::DependencyChecker; use backup::backend_type::BackendType; // Check if backend is available let available = DependencyChecker::is_available(BackendType::Restic)?; // Get dependency information let info = DependencyChecker::get_info(BackendType::Restic)?; println!("Restic: {} ({})", info.version, info.path); // Get installation guide let guide = DependencyChecker::get_install_guide(BackendType::Restic); ``` ### Schedule Generation ```rust use backup::schedule::ScheduleGenerator; use backup::backend_type::InitSystemType; use std::path::Path; let schedule = ScheduleGenerator::generate( InitSystemType::Systemd, "my-backup", Path::new("/usr/local/bin/backup.sh"), "0 2 * * *", // 2 AM daily "backup" // username )?; println!("{}", schedule); ``` ### File Filtering ```rust use backup::filter::FileFilter; use std::path::Path; let filter = FileFilter::new( vec!["*.rs".to_string()], // Include patterns vec!["target/*".to_string(), "*.tmp".to_string()] // Exclude patterns )?; if filter.should_include(Path::new("src/main.rs")) { println!("File will be included"); } ``` ## Examples ### Complete Backup Workflow ```rust use backup::config::BackupConfig; use backup::backends::ResticBackend; use backup::backend::BackupBackend; #[tokio::main] async fn main() -> Result<(), Box> { // Load and validate config let config = BackupConfig::from_toml("backup.toml")?; config.validate()?; // Create backend let backend = ResticBackend::new(None); // Check availability if !backend.is_available().await? { eprintln!("Restic not available"); return Ok(()); } // Initialize repository backend.init_repository(&config.repository).await?; // Backup all targets for target in &config.targets { println!("Backing up: {}", target.name); let report = backend.backup(target).await?; if !report.success { eprintln!("Backup failed: {:?}", report.errors); } else { println!("Backup complete: {} files processed", report.files_processed); } } // Prune old snapshots let prune_report = backend.prune(&config.retention).await?; println!("Pruned {} snapshots, freed {} bytes", prune_report.snapshots_removed, prune_report.space_freed); // Verify repository integrity let check = backend.check().await?; if check.valid { println!("Repository is valid"); } else { eprintln!("Repository issues: {:?}", check.issues); } Ok(()) } ``` ### Restore from Snapshot ```rust use backup::backends::ResticBackend; use backup::backend::BackupBackend; use backup::types::RestoreOptions; use std::path::Path; #[tokio::main] async fn main() -> Result<(), Box> { let backend = ResticBackend::new(None); let options = RestoreOptions { overwrite: true, verify: true, restore_permissions: true, }; backend.restore( "snapshot-id-here", Path::new("/restore/location"), &options ).await?; println!("Restore complete"); Ok(()) } ``` ## Testing Run tests: ```bash cargo test ``` Run with logging: ```bash RUST_LOG=debug cargo test -- --nocapture ``` ## Environment Variables ### For Restic - `RESTIC_REPOSITORY`: Repository location - `RESTIC_PASSWORD`: Encryption password - `RESTIC_PASSWORD_FILE`: Password file path - `AWS_ACCESS_KEY_ID`: AWS credentials - `AWS_SECRET_ACCESS_KEY`: AWS credentials ### For BorgBackup - `BORG_REPO`: Repository location - `BORG_PASSPHRASE`: Encryption password - `BORG_PASSCOMMAND`: Command to retrieve password ### For Rsync - `RSYNC_RSH`: Remote shell (e.g., "ssh -i /path/to/key") ## Error Handling The crate uses `BackupResult` for error handling: ```rust use backup::error::BackupResult; fn some_operation() -> BackupResult<()> { // Operation returns BackupResult Ok(()) } // Handle specific errors match some_operation() { Err(backup::error::BackupError::BackendUnavailable(msg)) => { eprintln!("Backend not available: {}", msg); } Err(e) => eprintln!("Error: {}", e), Ok(()) => println!("Success"), } ``` ## Performance Considerations - **Restic**: Most feature-rich but slower due to encryption overhead - **BorgBackup**: Excellent compression and deduplication - **Rsync**: Fast for incremental transfers, best for local networks - **TAR**: Simple and fast for full backups, no incremental support - **CPIO**: Portable format, good for cross-system backups ## Limitations - **TAR/CPIO**: No incremental backup support; full backup each time - **CPIO**: Limited metadata support compared to restic/borg - **Rsync**: No built-in deduplication - **Schedule generation**: Simple cron conversion; complex expressions need manual adjustment ## Contributing Contributions are welcome. Please ensure: - Code follows idioms - Tests are included for new features - Documentation is updated - Examples are provided ## License MIT License - See LICENSE file for details