Skip to main content

torrust_tracker_deployer_lib/application/services/rendering/
opentofu.rs

1//! `OpenTofu` Template Rendering Service
2//!
3//! This service is responsible for rendering `OpenTofu` (Terraform) infrastructure templates.
4//! It's used by multiple contexts (render command, provision steps) to prepare
5//! infrastructure-as-code files.
6
7use std::path::PathBuf;
8use std::sync::Arc;
9
10use thiserror::Error;
11use tracing::info;
12
13use crate::adapters::ssh::SshCredentials;
14use crate::domain::provider::ProviderConfig;
15use crate::domain::InstanceName;
16use crate::domain::TemplateManager;
17use crate::infrastructure::templating::tofu::{TofuProjectGenerator, TofuProjectGeneratorError};
18use crate::shared::Clock;
19
20/// Errors that can occur during `OpenTofu` template rendering
21#[derive(Error, Debug)]
22pub enum OpenTofuTemplateRenderingServiceError {
23    /// Template rendering failed
24    #[error("Failed to render OpenTofu templates: {reason}")]
25    RenderingFailed {
26        /// Detailed reason for the failure
27        reason: String,
28    },
29}
30
31impl From<TofuProjectGeneratorError> for OpenTofuTemplateRenderingServiceError {
32    fn from(error: TofuProjectGeneratorError) -> Self {
33        Self::RenderingFailed {
34            reason: error.to_string(),
35        }
36    }
37}
38
39/// Service for rendering `OpenTofu` infrastructure templates
40///
41/// This service encapsulates the logic for rendering `OpenTofu` (Terraform)
42/// configuration files. It's designed to be shared across command handlers
43/// and steps that need to prepare infrastructure templates.
44///
45/// Note: `OpenTofu` requires more configuration than other template types because
46/// it needs provider-specific settings, SSH credentials, and instance metadata.
47pub struct OpenTofuTemplateRenderingService {
48    generator: TofuProjectGenerator,
49}
50
51impl OpenTofuTemplateRenderingService {
52    /// Build an `OpenTofuTemplateRenderingService` from configuration parameters
53    ///
54    /// # Arguments
55    ///
56    /// * `templates_dir` - Directory containing the source templates
57    /// * `build_dir` - Directory where rendered templates will be written
58    /// * `ssh_credentials` - SSH credentials for accessing the provisioned instance
59    /// * `ssh_port` - SSH port for the instance
60    /// * `instance_name` - Name of the instance to provision
61    /// * `provider_config` - Provider-specific configuration (LXD, Docker, etc.)
62    /// * `clock` - The clock for generating timestamps
63    ///
64    /// # Returns
65    ///
66    /// Returns a configured `OpenTofuTemplateRenderingService` ready for template rendering
67    #[must_use]
68    pub fn from_params(
69        templates_dir: PathBuf,
70        build_dir: PathBuf,
71        ssh_credentials: SshCredentials,
72        ssh_port: u16,
73        instance_name: InstanceName,
74        provider_config: ProviderConfig,
75        clock: Arc<dyn Clock>,
76    ) -> Self {
77        let template_manager = Arc::new(TemplateManager::new(templates_dir));
78
79        let generator = TofuProjectGenerator::new(
80            template_manager,
81            build_dir,
82            ssh_credentials,
83            ssh_port,
84            instance_name,
85            provider_config,
86            clock,
87        );
88
89        Self { generator }
90    }
91
92    /// Render `OpenTofu` infrastructure templates
93    ///
94    /// This renders the `OpenTofu` configuration files (main.tf, variables.tf, etc.)
95    /// to the build directory.
96    ///
97    /// # Errors
98    ///
99    /// Returns `OpenTofuTemplateRenderingServiceError::RenderingFailed` if template rendering fails.
100    pub async fn render(&self) -> Result<(), OpenTofuTemplateRenderingServiceError> {
101        info!("Rendering OpenTofu infrastructure templates");
102
103        self.generator.render().await?;
104
105        info!("OpenTofu infrastructure templates rendered successfully");
106
107        Ok(())
108    }
109}