Skip to main content

torrust_tracker_deployer_lib/adapters/tofu/
mod.rs

1//! `OpenTofu` infrastructure management wrapper
2//!
3//! This module provides a comprehensive interface for managing infrastructure using
4//! `OpenTofu` (the open-source Terraform fork), including plan, apply, destroy operations
5//! and JSON output parsing for instance information.
6//!
7//! ## Module Structure
8//!
9//! - `client` - Main `OpenTofuClient` for executing `OpenTofu` commands
10//! - `json_parser` - JSON output parsing for `OpenTofu` state and plan information
11//!
12//! ## Key Features
13//!
14//! - Full infrastructure lifecycle management (init, plan, apply, destroy)
15//! - State management and instance information extraction
16//! - Emergency cleanup operations for testing scenarios
17//! - Comprehensive error handling with detailed context
18
19use std::path::Path;
20
21pub mod client;
22pub mod json_parser;
23
24// Re-export the main types for easier access
25pub use client::{InstanceInfo, OpenTofuClient, OpenTofuError};
26pub use json_parser::ParseError;
27
28/// Errors that can occur during emergency destroy operations
29#[derive(Debug)]
30pub enum EmergencyDestroyError {
31    /// Command execution failed (e.g., tofu binary not found)
32    CommandExecution { source: std::io::Error },
33
34    /// `OpenTofu` destroy operation failed with error output
35    DestroyFailed { stderr: String },
36}
37
38impl std::fmt::Display for EmergencyDestroyError {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        match self {
41            Self::CommandExecution { source } => {
42                write!(f, "Failed to execute OpenTofu destroy command: {source}")
43            }
44            Self::DestroyFailed { stderr } => {
45                write!(f, "OpenTofu destroy failed: {stderr}")
46            }
47        }
48    }
49}
50
51impl std::error::Error for EmergencyDestroyError {
52    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
53        match self {
54            Self::CommandExecution { source } => Some(source),
55            Self::DestroyFailed { .. } => None,
56        }
57    }
58}
59
60/// Emergency destroy operation for cleanup scenarios
61///
62/// This function performs a destructive `OpenTofu` destroy operation without prompting.
63/// It's designed for use in Drop implementations and other cleanup scenarios where
64/// interactive confirmation is not possible.
65///
66/// # Arguments
67///
68/// * `working_dir` - Directory containing the `OpenTofu` configuration files
69///
70/// # Returns
71///
72/// * `Result<(), EmergencyDestroyError>` - Success or concrete error from the destroy operation
73///
74/// # Errors
75///
76/// Returns an error if the `OpenTofu` destroy command fails or if there are issues
77/// with command execution.
78pub fn emergency_destroy<P: AsRef<Path>>(working_dir: P) -> Result<(), EmergencyDestroyError> {
79    use std::process::Command;
80
81    tracing::debug!(
82        "Emergency destroy: Executing `OpenTofu` destroy in directory: {}",
83        working_dir.as_ref().display()
84    );
85
86    let output = Command::new("tofu")
87        .args(["destroy", "-auto-approve"])
88        .current_dir(&working_dir)
89        .output()
90        .map_err(|source| EmergencyDestroyError::CommandExecution { source })?;
91
92    if output.status.success() {
93        tracing::debug!("Emergency destroy: `OpenTofu` destroy completed successfully");
94        Ok(())
95    } else {
96        let stderr = String::from_utf8_lossy(&output.stderr).to_string();
97        tracing::error!("Emergency destroy: `OpenTofu` destroy failed: {stderr}");
98        Err(EmergencyDestroyError::DestroyFailed { stderr })
99    }
100}