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}