431 lines
11 KiB
Markdown
431 lines
11 KiB
Markdown
# 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<dyn std::error::Error>> {
|
|
// 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<bool>;
|
|
async fn init_repository(&self, repo: &RepositoryConfig) -> BackupResult<()>;
|
|
async fn backup(&self, target: &BackupTarget) -> BackupResult<BackupReport>;
|
|
async fn list_snapshots(&self) -> BackupResult<Vec<Snapshot>>;
|
|
async fn restore(&self, snapshot_id: &str, target_path: &Path, options: &RestoreOptions) -> BackupResult<()>;
|
|
async fn prune(&self, policy: &RetentionPolicy) -> BackupResult<PruneReport>;
|
|
async fn check(&self) -> BackupResult<CheckReport>;
|
|
async fn stats(&self) -> BackupResult<RepositoryStats>;
|
|
}
|
|
```
|
|
|
|
### 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<dyn std::error::Error>> {
|
|
// 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<dyn std::error::Error>> {
|
|
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<T>` 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
|