server_forge/
containerization.rs

1//! # Containerization Module
2//!
3//! This module provides functionality for setting up and managing containerization
4//! technologies, specifically Docker and Kubernetes, on a Linux server. It includes
5//! functions for installation, configuration, and deployment of containerized applications.
6//!
7//! The module is designed to work across different Linux distributions by leveraging
8//! the appropriate package manager and installation methods for each system.
9
10use crate::config::Config;
11use crate::distro::{get_package_manager, PackageManager};
12use crate::rollback::RollbackManager;
13use crate::utils::run_command;
14use log::info;
15use std::error::Error;
16
17/// Sets up Docker on the system.
18///
19/// This function installs Docker, configures it, and ensures it's running and enabled on boot.
20/// It creates a snapshot before installation for potential rollback.
21///
22/// # Arguments
23///
24/// * `rollback` - A reference to the `RollbackManager` for creating snapshots
25///
26/// # Returns
27///
28/// Returns `Ok(())` if Docker is set up successfully, or an error if setup fails.
29pub fn setup_docker(rollback: &RollbackManager) -> Result<(), Box<dyn Error>> {
30    info!("Setting up Docker...");
31
32    let snapshot = rollback.create_snapshot()?;
33
34    install_docker()?;
35    configure_docker()?;
36
37    rollback.commit_snapshot(snapshot)?;
38
39    info!("Docker setup completed");
40    Ok(())
41}
42
43/// Sets up Kubernetes on the system.
44///
45/// This function installs Kubernetes tools (kubectl and minikube), configures them,
46/// and ensures they're ready for use. It creates a snapshot before installation for potential rollback.
47///
48/// # Arguments
49///
50/// * `rollback` - A reference to the `RollbackManager` for creating snapshots
51///
52/// # Returns
53///
54/// Returns `Ok(())` if Kubernetes is set up successfully, or an error if setup fails.
55pub fn setup_kubernetes(rollback: &RollbackManager) -> Result<(), Box<dyn Error>> {
56    info!("Setting up Kubernetes...");
57
58    let snapshot = rollback.create_snapshot()?;
59
60    install_kubernetes()?;
61    configure_kubernetes()?;
62
63    rollback.commit_snapshot(snapshot)?;
64
65    info!("Kubernetes setup completed");
66    Ok(())
67}
68
69/// Deploys containers for all applications specified in the configuration.
70///
71/// This function iterates through the list of applications in the configuration
72/// and deploys each as a container, either using Docker or Kubernetes based on the configuration.
73///
74/// # Arguments
75///
76/// * `config` - A reference to the `Config` struct containing deployment information
77/// * `rollback` - A reference to the `RollbackManager` for creating snapshots
78///
79/// # Returns
80///
81/// Returns `Ok(())` if all containers are deployed successfully, or an error if any deployment fails.
82pub fn deploy_containers(
83    config: &Config,
84    rollback: &RollbackManager,
85) -> Result<(), Box<dyn Error>> {
86    info!("Deploying containers...");
87    let snapshot = rollback.create_snapshot()?;
88
89    for app in &config.deployed_apps {
90        deploy_container(app, config.use_kubernetes)?;
91    }
92
93    rollback.commit_snapshot(snapshot)?;
94
95    info!("Container deployment completed");
96    Ok(())
97}
98
99/// Installs Docker on the system.
100///
101/// This function installs Docker using the appropriate method for the current Linux distribution.
102/// It adds the Docker repository, installs necessary dependencies, and installs Docker components.
103///
104/// # Returns
105///
106/// Returns `Ok(())` if Docker is installed successfully, or an error if installation fails.
107pub fn install_docker() -> Result<(), Box<dyn Error>> {
108    let package_manager = get_package_manager()?;
109
110    match package_manager {
111        PackageManager::Apt => {
112            run_command("apt", &["update"])?;
113            run_command(
114                "apt",
115                &[
116                    "install",
117                    "-y",
118                    "apt-transport-https",
119                    "ca-certificates",
120                    "curl",
121                    "gnupg",
122                    "lsb-release",
123                ],
124            )?;
125            run_command(
126                "curl",
127                &[
128                    "-fsSL",
129                    "https://download.docker.com/linux/ubuntu/gpg",
130                    "|",
131                    "gpg",
132                    "--dearmor",
133                    "-o",
134                    "/usr/share/keyrings/docker-archive-keyring.gpg",
135                ],
136            )?;
137            run_command("echo", &["\"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable\"", "|", "tee", "/etc/apt/sources.list.d/docker.list", ">", "/dev/null"])?;
138            run_command("apt", &["update"])?;
139            run_command(
140                "apt",
141                &[
142                    "install",
143                    "-y",
144                    "docker-ce",
145                    "docker-ce-cli",
146                    "containerd.io",
147                ],
148            )?;
149        }
150        PackageManager::Yum => {
151            run_command("yum", &["install", "-y", "yum-utils"])?;
152            run_command(
153                "yum-config-manager",
154                &[
155                    "--add-repo",
156                    "https://download.docker.com/linux/centos/docker-ce.repo",
157                ],
158            )?;
159            run_command(
160                "yum",
161                &[
162                    "install",
163                    "-y",
164                    "docker-ce",
165                    "docker-ce-cli",
166                    "containerd.io",
167                ],
168            )?;
169        }
170        PackageManager::Dnf => {
171            run_command("dnf", &["install", "-y", "dnf-plugins-core"])?;
172            run_command(
173                "dnf",
174                &[
175                    "config-manager",
176                    "--add-repo",
177                    "https://download.docker.com/linux/fedora/docker-ce.repo",
178                ],
179            )?;
180            run_command(
181                "dnf",
182                &[
183                    "install",
184                    "-y",
185                    "docker-ce",
186                    "docker-ce-cli",
187                    "containerd.io",
188                ],
189            )?;
190        }
191    }
192
193    run_command("systemctl", &["start", "docker"])?;
194    run_command("systemctl", &["enable", "docker"])?;
195
196    Ok(())
197}
198
199/// Configures Docker after installation.
200///
201/// This function sets up the Docker daemon with optimal settings, creates a Docker group,
202/// adds the current user to the Docker group, and restarts the Docker service to apply changes.
203///
204/// # Returns
205///
206/// Returns `Ok(())` if Docker is configured successfully, or an error if configuration fails.
207pub fn configure_docker() -> Result<(), Box<dyn Error>> {
208    // Create docker group if it doesn't exist
209    run_command("groupadd", &["docker"])?;
210
211    // Add current user to docker group
212    run_command("usermod", &["-aG", "docker", "$USER"])?;
213
214    // Set up Docker daemon configuration
215    let daemon_config = r#"
216{
217  "log-driver": "json-file",
218  "log-opts": {
219    "max-size": "100m",
220    "max-file": "3"
221  },
222  "default-ulimits": {
223    "nofile": {
224      "Name": "nofile",
225      "Hard": 64000,
226      "Soft": 64000
227    }
228  }
229}
230"#;
231    std::fs::write("/etc/docker/daemon.json", daemon_config)?;
232
233    // Restart Docker to apply changes
234    run_command("systemctl", &["restart", "docker"])?;
235
236    Ok(())
237}
238
239/// Installs Kubernetes tools (kubectl and minikube) on the system.
240///
241/// This function downloads and installs kubectl and minikube, and installs a virtualization
242/// driver (VirtualBox in this implementation) required for running Kubernetes locally.
243///
244/// # Returns
245///
246/// Returns `Ok(())` if Kubernetes tools are installed successfully, or an error if installation fails.
247pub fn install_kubernetes() -> Result<(), Box<dyn Error>> {
248    let package_manager = get_package_manager()?;
249
250    // Install kubectl
251    run_command("curl", &["-LO", "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl"])?;
252    run_command("chmod", &["+x", "./kubectl"])?;
253    run_command("mv", &["./kubectl", "/usr/local/bin/kubectl"])?;
254
255    // Install minikube
256    run_command(
257        "curl",
258        &[
259            "-Lo",
260            "minikube",
261            "https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64",
262        ],
263    )?;
264    run_command("chmod", &["+x", "minikube"])?;
265    run_command("mv", &["minikube", "/usr/local/bin/"])?;
266
267    // Install required virtualization driver (using VirtualBox in this example)
268    match package_manager {
269        PackageManager::Apt => run_command("apt", &["install", "-y", "virtualbox"])?,
270        PackageManager::Yum => run_command("yum", &["install", "-y", "VirtualBox"])?,
271        PackageManager::Dnf => run_command("dnf", &["install", "-y", "VirtualBox"])?,
272    }
273
274    Ok(())
275}
276
277/// Configures Kubernetes after installation.
278///
279/// This function starts minikube, enables necessary addons (ingress and dashboard),
280/// and sets up kubectl autocomplete for easier use.
281///
282/// # Returns
283///
284/// Returns `Ok(())` if Kubernetes is configured successfully, or an error if configuration fails.
285pub fn configure_kubernetes() -> Result<(), Box<dyn Error>> {
286    // Start minikube
287    run_command("minikube", &["start"])?;
288
289    // Enable necessary addons
290    run_command("minikube", &["addons", "enable", "ingress"])?;
291    run_command("minikube", &["addons", "enable", "dashboard"])?;
292
293    // Set up kubectl autocomplete
294    run_command(
295        "kubectl",
296        &["completion", "bash", ">", "/etc/bash_completion.d/kubectl"],
297    )?;
298
299    Ok(())
300}
301
302/// Deploys a single container for the specified application.
303///
304/// This function deploys the application either to Kubernetes or directly to Docker,
305/// based on the `use_kubernetes` flag.
306///
307/// # Arguments
308///
309/// * `app` - A string slice representing the application to deploy
310/// * `use_kubernetes` - A boolean indicating whether to use Kubernetes for deployment
311///
312/// # Returns
313///
314/// Returns `Ok(())` if the container is deployed successfully, or an error if deployment fails.
315pub fn deploy_container(app: &str, use_kubernetes: bool) -> Result<(), Box<dyn Error>> {
316    if use_kubernetes {
317        deploy_to_kubernetes(app)?;
318    } else {
319        deploy_to_docker(app)?;
320    }
321    Ok(())
322}
323
324/// Deploys a single container for the specified application.
325///
326/// This function deploys the application either to Kubernetes or directly to Docker,
327/// based on the `use_kubernetes` flag.
328///
329/// # Arguments
330///
331/// * `app` - A string slice representing the application to deploy
332/// * `use_kubernetes` - A boolean indicating whether to use Kubernetes for deployment
333///
334/// # Returns
335///
336/// Returns `Ok(())` if the container is deployed successfully, or an error if deployment fails.
337pub fn deploy_to_kubernetes(app: &str) -> Result<(), Box<dyn Error>> {
338    // Create a basic deployment YAML
339    let deployment_yaml = format!(
340        r#"
341apiVersion: apps/v1
342kind: Deployment
343metadata:
344  name: {}
345spec:
346  replicas: 1
347  selector:
348    matchLabels:
349      app: {}
350  template:
351    metadata:
352      labels:
353        app: {}
354    spec:
355      containers:
356      - name: {}
357        image: {}:latest
358        ports:
359        - containerPort: 80
360"#,
361        app, app, app, app, app
362    );
363
364    // Write the deployment YAML to a file
365    std::fs::write(format!("{}-deployment.yaml", app), deployment_yaml)?;
366
367    // Apply the deployment
368    run_command(
369        "kubectl",
370        &["apply", "-f", &format!("{}-deployment.yaml", app)],
371    )?;
372
373    // Expose the deployment as a service
374    run_command(
375        "kubectl",
376        &[
377            "expose",
378            "deployment",
379            app,
380            "--type=LoadBalancer",
381            "--port=80",
382        ],
383    )?;
384
385    Ok(())
386}
387
388/// Deploys an application to Kubernetes.
389///
390/// This function creates a Kubernetes Deployment and Service for the specified application.
391/// It generates a basic YAML configuration, applies it to the cluster, and exposes the deployment as a service.
392///
393/// # Arguments
394///
395/// * `app` - A string slice representing the application to deploy
396///
397/// # Returns
398///
399/// Returns `Ok(())` if the application is deployed to Kubernetes successfully, or an error if deployment fails.
400pub fn deploy_to_docker(app: &str) -> Result<(), Box<dyn Error>> {
401    // Pull the latest image
402    run_command("docker", &["pull", app])?;
403
404    // Stop and remove any existing container with the same name
405    run_command("docker", &["stop", app]).ok();
406    run_command("docker", &["rm", app]).ok();
407
408    // Run the new container
409    run_command("docker", &["run", "-d", "--name", app, "-p", "80:80", app])?;
410
411    Ok(())
412}