torrust_tracker_deployer_lib/application/steps/rendering/
prometheus_templates.rs1use std::path::PathBuf;
28use std::sync::Arc;
29
30use tracing::{info, instrument};
31
32use crate::application::services::rendering::PrometheusTemplateRenderingService;
33use crate::application::services::rendering::PrometheusTemplateRenderingServiceError;
34use crate::domain::environment::Environment;
35use crate::shared::clock::Clock;
36
37pub struct RenderPrometheusTemplatesStep<S> {
43 environment: Arc<Environment<S>>,
44 templates_dir: PathBuf,
45 build_dir: PathBuf,
46 clock: Arc<dyn Clock>,
47}
48
49impl<S> RenderPrometheusTemplatesStep<S> {
50 #[must_use]
59 pub fn new(
60 environment: Arc<Environment<S>>,
61 templates_dir: PathBuf,
62 build_dir: PathBuf,
63 clock: Arc<dyn Clock>,
64 ) -> Self {
65 Self {
66 environment,
67 templates_dir,
68 build_dir,
69 clock,
70 }
71 }
72
73 #[instrument(
90 name = "render_prometheus_templates",
91 skip_all,
92 fields(
93 step_type = "rendering",
94 template_type = "prometheus",
95 build_dir = %self.build_dir.display()
96 )
97 )]
98 pub fn execute(&self) -> Result<Option<PathBuf>, PrometheusTemplateRenderingServiceError> {
99 let Some(prometheus_config) = self.environment.context().user_inputs.prometheus() else {
101 info!(
102 step = "render_prometheus_templates",
103 status = "skipped",
104 reason = "prometheus_not_configured",
105 "Skipping Prometheus template rendering - not configured"
106 );
107 return Ok(None);
108 };
109
110 info!(
111 step = "render_prometheus_templates",
112 templates_dir = %self.templates_dir.display(),
113 build_dir = %self.build_dir.display(),
114 "Rendering Prometheus configuration templates"
115 );
116
117 let service = PrometheusTemplateRenderingService::from_paths(
118 self.templates_dir.clone(),
119 self.build_dir.clone(),
120 self.clock.clone(),
121 );
122
123 let tracker_config = self.environment.context().user_inputs.tracker();
125 let Some(prometheus_build_dir) = service.render(Some(prometheus_config), tracker_config)?
126 else {
127 return Ok(None);
128 };
129
130 info!(
131 step = "render_prometheus_templates",
132 prometheus_build_dir = %prometheus_build_dir.display(),
133 status = "success",
134 "Prometheus templates rendered successfully"
135 );
136
137 Ok(Some(prometheus_build_dir))
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use tempfile::TempDir;
144
145 use super::*;
146 use crate::domain::environment::testing::EnvironmentTestBuilder;
147 use crate::domain::prometheus::PrometheusConfig;
148 use crate::testing::mock_clock::MockClock;
149
150 fn create_test_clock() -> Arc<dyn Clock> {
151 use chrono::TimeZone;
152 let fixed_time = chrono::Utc
153 .with_ymd_and_hms(2026, 1, 27, 13, 41, 56)
154 .unwrap();
155 Arc::new(MockClock::new(fixed_time))
156 }
157
158 #[test]
159 fn it_should_create_render_prometheus_templates_step() {
160 let templates_dir = TempDir::new().expect("Failed to create templates dir");
161 let build_dir = TempDir::new().expect("Failed to create build dir");
162
163 let (environment, _, _, _temp_dir) =
164 EnvironmentTestBuilder::new().build_with_custom_paths();
165 let environment = Arc::new(environment);
166
167 let clock = create_test_clock();
168 let step = RenderPrometheusTemplatesStep::new(
169 environment.clone(),
170 templates_dir.path().to_path_buf(),
171 build_dir.path().to_path_buf(),
172 clock,
173 );
174
175 assert_eq!(step.build_dir, build_dir.path());
176 assert_eq!(step.templates_dir, templates_dir.path());
177 }
178
179 #[test]
180 fn it_should_skip_rendering_when_prometheus_not_configured() {
181 let templates_dir = TempDir::new().expect("Failed to create templates dir");
182 let build_dir = TempDir::new().expect("Failed to create build dir");
183
184 let (environment, _, _, _temp_dir) = EnvironmentTestBuilder::new()
186 .with_prometheus_config(None)
187 .build_with_custom_paths();
188 let environment = Arc::new(environment);
189
190 let clock = create_test_clock();
191 let step = RenderPrometheusTemplatesStep::new(
192 environment,
193 templates_dir.path().to_path_buf(),
194 build_dir.path().to_path_buf(),
195 clock,
196 );
197
198 let result = step.execute();
199 assert!(
200 result.is_ok(),
201 "Should succeed when Prometheus not configured"
202 );
203 assert!(
204 result.unwrap().is_none(),
205 "Should return None when Prometheus not configured"
206 );
207 }
208
209 #[test]
210 fn it_should_render_templates_when_prometheus_configured() {
211 let templates_dir = TempDir::new().expect("Failed to create templates dir");
212 let build_dir = TempDir::new().expect("Failed to create build dir");
213
214 let (environment, _, _, _temp_dir) = EnvironmentTestBuilder::new()
216 .with_prometheus_config(Some(PrometheusConfig::new(
217 std::num::NonZeroU32::new(30).expect("30 is non-zero"),
218 )))
219 .build_with_custom_paths();
220 let environment = Arc::new(environment);
221
222 let clock = create_test_clock();
223 let step = RenderPrometheusTemplatesStep::new(
224 environment,
225 templates_dir.path().to_path_buf(),
226 build_dir.path().to_path_buf(),
227 clock,
228 );
229
230 let result = step.execute();
231 assert!(result.is_ok(), "Should render Prometheus templates");
232
233 let prometheus_build_dir = result.unwrap();
234 assert!(
235 prometheus_build_dir.is_some(),
236 "Should return build directory path"
237 );
238
239 let build_dir_path = prometheus_build_dir.unwrap();
240 assert!(
241 build_dir_path.to_string_lossy().contains("prometheus"),
242 "Build directory should contain 'prometheus' in path"
243 );
244 }
245}