torrust_tracker_deployer_lib/application/steps/rendering/
ansible_templates.rs1use std::net::SocketAddr;
22use std::sync::Arc;
23
24use thiserror::Error;
25use tracing::{info, instrument};
26
27use crate::adapters::ssh::credentials::SshCredentials;
28use crate::domain::grafana::GrafanaConfig;
29use crate::domain::tracker::TrackerConfig;
30use crate::infrastructure::templating::ansible::template::renderer::AnsibleProjectGeneratorError;
31use crate::infrastructure::templating::ansible::template::wrappers::inventory::{
32 AnsibleHost, AnsiblePort, AnsiblePortError, InventoryContext, InventoryContextError,
33 SshPrivateKeyFile, SshPrivateKeyFileError,
34};
35use crate::infrastructure::templating::ansible::AnsibleProjectGenerator;
36use crate::infrastructure::templating::TemplateMetadata;
37use crate::shared::clock::Clock;
38
39#[derive(Error, Debug)]
41pub enum RenderAnsibleTemplatesError {
42 #[error("SSH key path parsing failed: {0}")]
44 SshKeyPathError(#[from] SshPrivateKeyFileError),
45
46 #[error("SSH port parsing failed: {0}")]
48 SshPortError(#[from] AnsiblePortError),
49
50 #[error("Inventory context creation failed: {0}")]
52 InventoryContextError(#[from] InventoryContextError),
53
54 #[error("Template rendering failed: {0}")]
56 TemplateRenderingError(#[from] AnsibleProjectGeneratorError),
57}
58
59impl crate::shared::Traceable for RenderAnsibleTemplatesError {
60 fn trace_format(&self) -> String {
61 match self {
62 Self::SshKeyPathError(e) => {
63 format!("RenderAnsibleTemplatesError: SSH key path parsing failed - {e}")
64 }
65 Self::SshPortError(e) => {
66 format!("RenderAnsibleTemplatesError: SSH port parsing failed - {e}")
67 }
68 Self::InventoryContextError(e) => {
69 format!("RenderAnsibleTemplatesError: Inventory context creation failed - {e}")
70 }
71 Self::TemplateRenderingError(e) => {
72 format!("RenderAnsibleTemplatesError: Template rendering failed - {e}")
73 }
74 }
75 }
76
77 fn trace_source(&self) -> Option<&dyn crate::shared::Traceable> {
78 None
80 }
81
82 fn error_kind(&self) -> crate::shared::ErrorKind {
83 crate::shared::ErrorKind::TemplateRendering
84 }
85}
86
87pub struct RenderAnsibleTemplatesStep {
89 ansible_project_generator: Arc<AnsibleProjectGenerator>,
90 ssh_credentials: SshCredentials,
91 ssh_socket_addr: SocketAddr,
92 tracker_config: TrackerConfig,
93 grafana_config: Option<GrafanaConfig>,
94 clock: Arc<dyn Clock>,
95}
96
97impl RenderAnsibleTemplatesStep {
98 #[must_use]
99 pub fn new(
100 ansible_project_generator: Arc<AnsibleProjectGenerator>,
101 ssh_credentials: SshCredentials,
102 ssh_socket_addr: SocketAddr,
103 tracker_config: TrackerConfig,
104 grafana_config: Option<GrafanaConfig>,
105 clock: Arc<dyn Clock>,
106 ) -> Self {
107 Self {
108 ansible_project_generator,
109 ssh_credentials,
110 ssh_socket_addr,
111 tracker_config,
112 grafana_config,
113 clock,
114 }
115 }
116
117 #[instrument(
124 name = "render_ansible_templates",
125 skip_all,
126 fields(step_type = "rendering", template_type = "ansible")
127 )]
128 pub async fn execute(&self) -> Result<(), RenderAnsibleTemplatesError> {
129 info!(
130 step = "render_ansible_templates",
131 "Rendering Ansible templates with runtime variables"
132 );
133
134 let inventory_context = self.create_inventory_context()?;
136
137 self.ansible_project_generator
139 .render(
140 &inventory_context,
141 Some(&self.tracker_config),
142 self.grafana_config.as_ref(),
143 )
144 .await?;
145
146 info!(
147 step = "render_ansible_templates",
148 status = "success",
149 "Ansible templates rendered successfully"
150 );
151
152 Ok(())
153 }
154
155 fn create_inventory_context(&self) -> Result<InventoryContext, RenderAnsibleTemplatesError> {
163 let metadata = TemplateMetadata::new(self.clock.now());
164 let host = AnsibleHost::from(self.ssh_socket_addr.ip());
165 let ssh_key = SshPrivateKeyFile::new(&self.ssh_credentials.ssh_priv_key_path)?;
166 let ssh_port = AnsiblePort::new(self.ssh_socket_addr.port())?;
167 let ansible_user = self.ssh_credentials.ssh_username.as_str().to_string();
168
169 InventoryContext::builder()
170 .with_metadata(metadata)
171 .with_host(host)
172 .with_ssh_priv_key_path(ssh_key)
173 .with_ssh_port(ssh_port)
174 .with_ansible_user(ansible_user)
175 .build()
176 .map_err(RenderAnsibleTemplatesError::from)
177 }
178}