torrust_tracker_deployer_lib/application/steps/infrastructure/
apply.rs1use std::sync::Arc;
25
26use tracing::{info, instrument};
27
28use crate::adapters::tofu::client::OpenTofuClient;
29use crate::application::traits::CommandProgressListener;
30use crate::shared::command::CommandError;
31
32pub struct ApplyInfrastructureStep {
34 opentofu_client: Arc<OpenTofuClient>,
35 auto_approve: bool,
36}
37
38impl ApplyInfrastructureStep {
39 #[must_use]
40 pub fn new(opentofu_client: Arc<OpenTofuClient>) -> Self {
41 Self {
42 opentofu_client,
43 auto_approve: true, }
45 }
46
47 #[must_use]
48 pub fn with_auto_approve(mut self, auto_approve: bool) -> Self {
49 self.auto_approve = auto_approve;
50 self
51 }
52
53 #[instrument(
66 name = "apply_infrastructure",
67 skip_all,
68 fields(
69 step_type = "infrastructure",
70 operation = "apply",
71 auto_approve = %self.auto_approve
72 )
73 )]
74 pub fn execute(
75 &self,
76 listener: Option<&dyn CommandProgressListener>,
77 ) -> Result<(), CommandError> {
78 info!(
79 step = "apply_infrastructure",
80 auto_approve = self.auto_approve,
81 "Applying OpenTofu infrastructure"
82 );
83
84 if let Some(l) = listener {
85 l.on_debug(&format!(
86 "Working directory: {}",
87 self.opentofu_client.working_dir().display()
88 ));
89 l.on_debug(&format!(
90 "Executing: tofu apply -var-file=variables.tfvars{}",
91 if self.auto_approve {
92 " -auto-approve"
93 } else {
94 ""
95 }
96 ));
97 }
98
99 let output = self
101 .opentofu_client
102 .apply(self.auto_approve, &["-var-file=variables.tfvars"])?;
103
104 if let Some(l) = listener {
106 if output.contains("Creation complete") || output.contains("Modifications complete") {
108 l.on_detail("Infrastructure resources created successfully");
109 } else if output.contains("Apply complete") {
110 l.on_detail("Infrastructure applied successfully");
111 }
112 }
113
114 info!(
115 step = "apply_infrastructure",
116 status = "success",
117 "OpenTofu infrastructure applied successfully"
118 );
119
120 tracing::debug!(output = %output, "OpenTofu apply output");
122
123 Ok(())
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use std::sync::Arc;
130
131 use crate::adapters::tofu::client::OpenTofuClient;
132
133 use super::*;
134
135 #[test]
136 fn it_should_create_apply_infrastructure_step() {
137 let opentofu_client = Arc::new(OpenTofuClient::new("/tmp"));
138
139 let _step = ApplyInfrastructureStep::new(opentofu_client);
140
141 }
143
144 #[test]
145 fn it_should_create_step_with_custom_auto_approve() {
146 let opentofu_client = Arc::new(OpenTofuClient::new("/tmp"));
147
148 let step = ApplyInfrastructureStep::new(opentofu_client).with_auto_approve(false);
149
150 assert!(!step.auto_approve);
151 }
152
153 #[test]
154 fn it_should_default_to_auto_approve() {
155 let opentofu_client = Arc::new(OpenTofuClient::new("/tmp"));
156
157 let step = ApplyInfrastructureStep::new(opentofu_client);
158
159 assert!(step.auto_approve);
160 }
161}