1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! # Backup Module
//!
//! This module provides functionality for setting up and configuring an automated backup system
//! for a Linux server. It uses `restic` as the backup tool, which provides efficient, encrypted,
//! and incremental backups.
//!
//! The module includes functions for installing backup tools, configuring backup schedules,
//! and setting up backup locations based on the server's role.

use crate::config::Config;
use crate::distro::{get_package_manager, PackageManager};
use crate::rollback::RollbackManager;
use crate::utils::run_command;
use log::info;
use std::error::Error;

/// Sets up the backup system based on the provided configuration.
///
/// This function orchestrates the entire backup setup process, including:
/// - Installing necessary backup tools
/// - Configuring the backup schedule
/// - Setting up backup locations
///
/// It creates a snapshot before starting the setup process for potential rollback.
///
/// # Arguments
///
/// * `config` - A reference to the `Config` struct containing backup configuration
/// * `rollback` - A reference to the `RollbackManager` for creating snapshots
///
/// # Returns
///
/// Returns `Ok(())` if the backup system is set up successfully, or an error if setup fails.
pub fn setup_backup_system(
    config: &Config,
    rollback: &RollbackManager,
) -> Result<(), Box<dyn Error>> {
    info!("Setting up backup system...");

    let snapshot = rollback.create_snapshot()?;

    install_backup_tools()?;
    configure_backup_schedule(config)?;
    setup_backup_locations(config)?;

    rollback.commit_snapshot(snapshot)?;

    info!("Backup system setup completed");
    Ok(())
}

/// Installs the necessary backup tools (restic) on the system.
///
/// This function uses the appropriate package manager for the current Linux distribution
/// to install restic.
///
/// # Returns
///
/// Returns `Ok(())` if restic is installed successfully, or an error if installation fails.
pub fn install_backup_tools() -> Result<(), Box<dyn Error>> {
    let package_manager = get_package_manager()?;
    match package_manager {
        PackageManager::Apt => run_command("apt", &["install", "-y", "restic"])?,
        PackageManager::Yum => run_command("yum", &["install", "-y", "restic"])?,
        PackageManager::Dnf => run_command("dnf", &["install", "-y", "restic"])?,
    }
    Ok(())
}

/// Configures the backup schedule based on the provided configuration.
///
/// This function creates a cron job for running backups at the specified frequency
/// (hourly, daily, or weekly).
///
/// # Arguments
///
/// * `config` - A reference to the `Config` struct containing the backup frequency
///
/// # Returns
///
/// Returns `Ok(())` if the backup schedule is configured successfully, or an error if configuration fails.
pub fn configure_backup_schedule(config: &Config) -> Result<(), Box<dyn Error>> {
    let cron_job = match config.backup_frequency.as_str() {
        "hourly" => {
            "0 * * * * root /usr/bin/restic backup /path/to/backup >> /var/log/restic.log 2>&1\n"
        }
        "daily" => {
            "0 2 * * * root /usr/bin/restic backup /path/to/backup >> /var/log/restic.log 2>&1\n"
        }
        "weekly" => {
            "0 2 * * 0 root /usr/bin/restic backup /path/to/backup >> /var/log/restic.log 2>&1\n"
        }
        _ => return Err("Invalid backup frequency".into()),
    };

    std::fs::write("/etc/cron.d/restic-backup", cron_job)?;
    Ok(())
}

/// Sets up backup locations based on the server's role.
///
/// This function determines which directories to back up based on the server's role
/// (web, database, or application server). It then initializes a restic repository
/// and creates a backup script that includes these locations.
///
/// # Arguments
///
/// * `config` - A reference to the `Config` struct containing the server role
///
/// # Returns
///
/// Returns `Ok(())` if backup locations are set up successfully, or an error if setup fails.
pub fn setup_backup_locations(config: &Config) -> Result<(), Box<dyn Error>> {
    // Define backup locations based on server role
    let backup_dirs = match config.server_role.as_str() {
        "web" => vec!["/var/www", "/etc/nginx", "/etc/apache2"],
        "database" => vec!["/var/lib/mysql", "/var/lib/postgresql"],
        "application" => vec!["/opt/myapp", "/etc/myapp"],
        _ => vec![],
    };

    // Create restic repository
    run_command("restic", &["init", "--repo", "/path/to/backup/repository"])?;

    // Create backup script
    let mut backup_script = String::from("#!/bin/bash\n\n");
    backup_script.push_str("export RESTIC_PASSWORD='your_restic_password'\n\n");
    backup_script.push_str("restic backup");
    for dir in backup_dirs {
        backup_script.push_str(&format!(" {}", dir));
    }
    backup_script.push_str(" --tag serverforge\n");

    std::fs::write("/usr/local/bin/run-backup.sh", backup_script)?;
    run_command("chmod", &["+x", "/usr/local/bin/run-backup.sh"])?;

    Ok(())
}