torrust_tracker_deployer_lib/domain/environment/state/
destroying.rs1use serde::{Deserialize, Serialize};
13
14use crate::domain::environment::state::{
15 AnyEnvironmentState, DestroyFailed, Destroyed, StateTypeError,
16};
17use crate::domain::environment::Environment;
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28pub struct Destroying;
29
30impl Environment<Destroying> {
32 #[must_use]
36 pub fn destroyed(self) -> Environment<Destroyed> {
37 self.with_state(Destroyed)
38 }
39
40 #[must_use]
46 pub fn destroy_failed(
47 self,
48 context: crate::domain::environment::state::DestroyFailureContext,
49 ) -> Environment<DestroyFailed> {
50 self.with_state(DestroyFailed { context })
51 }
52}
53
54impl Environment<Destroying> {
56 #[must_use]
58 pub fn into_any(self) -> AnyEnvironmentState {
59 AnyEnvironmentState::Destroying(self)
60 }
61}
62
63impl AnyEnvironmentState {
65 pub fn try_into_destroying(self) -> Result<Environment<Destroying>, StateTypeError> {
71 match self {
72 Self::Destroying(env) => Ok(env),
73 other => Err(StateTypeError::UnexpectedState {
74 expected: "destroying",
75 actual: other.state_name().to_string(),
76 }),
77 }
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn it_should_create_destroying_state() {
87 let state = Destroying;
88 assert_eq!(state, Destroying);
89 }
90
91 mod conversion_tests {
92 use super::*;
93 use crate::adapters::ssh::SshCredentials;
94 use crate::domain::environment::name::EnvironmentName;
95 use crate::domain::environment::runtime_outputs::ProvisionMethod;
96 use crate::domain::provider::{LxdConfig, ProviderConfig};
97 use crate::domain::ProfileName;
98 use crate::shared::Username;
99 use std::net::{IpAddr, Ipv4Addr};
100 use std::path::PathBuf;
101
102 fn default_lxd_provider_config(env_name: &EnvironmentName) -> ProviderConfig {
103 ProviderConfig::Lxd(LxdConfig {
104 profile_name: ProfileName::new(format!("lxd-{}", env_name.as_str())).unwrap(),
105 })
106 }
107
108 fn create_test_ssh_credentials() -> SshCredentials {
109 let username = Username::new("test-user".to_string()).unwrap();
110 SshCredentials::new(
111 PathBuf::from("/tmp/test_key"),
112 PathBuf::from("/tmp/test_key.pub"),
113 username,
114 )
115 }
116
117 fn create_test_environment_destroying() -> Environment<Destroying> {
118 let name = EnvironmentName::new("test-env".to_string()).unwrap();
119 let ssh_creds = create_test_ssh_credentials();
120 Environment::new(
121 name.clone(),
122 default_lxd_provider_config(&name),
123 ssh_creds,
124 22,
125 chrono::Utc::now(),
126 )
127 .start_provisioning()
128 .provisioned(
129 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
130 ProvisionMethod::Provisioned,
131 )
132 .start_destroying()
133 }
134
135 #[test]
136 fn it_should_convert_destroying_environment_into_any() {
137 let env = create_test_environment_destroying();
138 let any_env = env.into_any();
139 assert!(matches!(any_env, AnyEnvironmentState::Destroying(_)));
140 }
141
142 #[test]
143 fn it_should_convert_any_to_destroying_successfully() {
144 let env = create_test_environment_destroying();
145 let any_env = env.into_any();
146 let result = any_env.try_into_destroying();
147 assert!(result.is_ok());
148 }
149
150 #[test]
151 fn it_should_fail_converting_created_to_destroying() {
152 let name = EnvironmentName::new("test-env".to_string()).unwrap();
153 let ssh_creds = create_test_ssh_credentials();
154 let env = Environment::new(
155 name.clone(),
156 default_lxd_provider_config(&name),
157 ssh_creds,
158 22,
159 chrono::Utc::now(),
160 );
161 let any_env = env.into_any();
162 let result = any_env.try_into_destroying();
163 assert!(result.is_err());
164 let err = result.unwrap_err();
165 assert!(err.to_string().contains("destroying"));
166 assert!(err.to_string().contains("created"));
167 }
168 }
169
170 mod state_transitions {
171 use super::super::*;
172 use crate::adapters::ssh::SshCredentials;
173 use crate::domain::environment::name::EnvironmentName;
174 use crate::domain::environment::runtime_outputs::ProvisionMethod;
175 use crate::domain::environment::state::Destroyed;
176 use crate::domain::provider::{LxdConfig, ProviderConfig};
177 use crate::domain::ProfileName;
178 use crate::shared::Username;
179 use std::net::{IpAddr, Ipv4Addr};
180 use std::path::PathBuf;
181
182 fn default_lxd_provider_config(env_name: &EnvironmentName) -> ProviderConfig {
183 ProviderConfig::Lxd(LxdConfig {
184 profile_name: ProfileName::new(format!("lxd-{}", env_name.as_str())).unwrap(),
185 })
186 }
187
188 fn create_test_ssh_credentials() -> SshCredentials {
189 let username = Username::new("test-user".to_string()).unwrap();
190 SshCredentials::new(
191 PathBuf::from("/tmp/test_key"),
192 PathBuf::from("/tmp/test_key.pub"),
193 username,
194 )
195 }
196
197 fn create_test_environment_destroying() -> Environment<Destroying> {
198 let name = EnvironmentName::new("test-env".to_string()).unwrap();
199 let ssh_creds = create_test_ssh_credentials();
200 Environment::new(
201 name.clone(),
202 default_lxd_provider_config(&name),
203 ssh_creds,
204 22,
205 chrono::Utc::now(),
206 )
207 .start_provisioning()
208 .provisioned(
209 IpAddr::V4(Ipv4Addr::new(192, 168, 1, 100)),
210 ProvisionMethod::Provisioned,
211 )
212 .start_destroying()
213 }
214
215 #[test]
216 fn it_should_transition_from_destroying_to_destroyed() {
217 let env = create_test_environment_destroying();
218 let env = env.destroyed();
219
220 assert_eq!(*env.state(), Destroyed);
221 assert_eq!(env.name().as_str(), "test-env");
222 }
223
224 #[test]
225 fn it_should_transition_from_destroying_to_destroy_failed() {
226 use crate::domain::environment::state::{
227 BaseFailureContext, DestroyFailureContext, DestroyStep,
228 };
229 use crate::domain::environment::TraceId;
230 use crate::shared::ErrorKind;
231 use chrono::Utc;
232 use std::time::Duration;
233
234 let env = create_test_environment_destroying();
235 let context = DestroyFailureContext {
236 failed_step: DestroyStep::DestroyInfrastructure,
237 error_kind: ErrorKind::InfrastructureOperation,
238 base: BaseFailureContext {
239 error_summary: "infrastructure_destroy_failed".to_string(),
240 failed_at: Utc::now(),
241 execution_started_at: Utc::now(),
242 execution_duration: Duration::from_secs(30),
243 trace_id: TraceId::new(),
244 trace_file_path: None,
245 },
246 };
247 let env = env.destroy_failed(context.clone());
248
249 assert_eq!(
250 env.state().context.failed_step,
251 DestroyStep::DestroyInfrastructure
252 );
253 assert_eq!(env.name().as_str(), "test-env");
254 }
255 }
256}