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