1use crate::types::{Bid, LeaseId, ServiceEndpoint};
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub enum Step {
13 Init,
15 CheckBalance,
17 EnsureCertificate,
19 CreateDeployment,
21 WaitForBids { waited_blocks: u32 },
23 SelectProvider,
25 CreateLease,
27 SendManifest,
29 WaitForEndpoints { attempts: u32 },
31 Complete,
33 Failed { reason: String, recoverable: bool },
35}
36
37impl Step {
38 pub fn name(&self) -> &'static str {
40 match self {
41 Step::Init => "init",
42 Step::CheckBalance => "check_balance",
43 Step::EnsureCertificate => "ensure_certificate",
44 Step::CreateDeployment => "create_deployment",
45 Step::WaitForBids { .. } => "wait_for_bids",
46 Step::SelectProvider => "select_provider",
47 Step::CreateLease => "create_lease",
48 Step::SendManifest => "send_manifest",
49 Step::WaitForEndpoints { .. } => "wait_for_endpoints",
50 Step::Complete => "complete",
51 Step::Failed { .. } => "failed",
52 }
53 }
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct DeploymentState {
59 pub session_id: String,
61 pub step: Step,
63 pub owner: String,
65 pub label: String,
67
68 pub sdl_content: Option<String>,
71
72 #[cfg(feature = "sdl-templates")]
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub template_variables: Option<std::collections::HashMap<String, String>>,
77
78 #[cfg(feature = "sdl-templates")]
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub template_defaults: Option<std::collections::HashMap<String, String>>,
82
83 #[cfg(feature = "sdl-templates")]
84 #[serde(default)]
85 pub is_template: bool,
87 pub deposit_uakt: u64,
89 pub dseq: Option<u64>,
91 pub gseq: u32,
93 pub oseq: u32,
95
96 pub cert_pem: Option<Vec<u8>>,
99 pub key_pem: Option<Vec<u8>>,
101
102 pub bids: Vec<Bid>,
105 pub selected_provider: Option<String>,
107
108 pub endpoints: Vec<ServiceEndpoint>,
111 pub lease_id: Option<LeaseId>,
113
114 pub created_at: u64,
117 pub updated_at: u64,
119 pub tx_hashes: Vec<String>,
121}
122
123impl DeploymentState {
124 pub fn new(session_id: impl Into<String>, owner: impl Into<String>) -> Self {
126 let now = current_unix_time();
127
128 Self {
129 session_id: session_id.into(),
130 step: Step::Init,
131 owner: owner.into(),
132 label: String::new(),
133 sdl_content: None,
134 deposit_uakt: 5_000_000, dseq: None,
136 gseq: 1,
137 oseq: 1,
138 cert_pem: None,
139 key_pem: None,
140 bids: Vec::new(),
141 selected_provider: None,
142 endpoints: Vec::new(),
143 lease_id: None,
144 created_at: now,
145 updated_at: now,
146 tx_hashes: Vec::new(),
147 #[cfg(feature = "sdl-templates")]
148 template_variables: None,
149 #[cfg(feature = "sdl-templates")]
150 template_defaults: None,
151 #[cfg(feature = "sdl-templates")]
152 is_template: false,
153 }
154 }
155
156 pub fn with_label(mut self, label: impl Into<String>) -> Self {
158 self.label = label.into();
159 self
160 }
161
162 pub fn with_sdl(mut self, sdl: impl Into<String>) -> Self {
164 self.sdl_content = Some(sdl.into());
165 self
166 }
167
168 pub fn with_deposit(mut self, deposit_uakt: u64) -> Self {
170 self.deposit_uakt = deposit_uakt;
171 self
172 }
173
174 #[cfg(feature = "sdl-templates")]
176 pub fn with_template(mut self, defaults: std::collections::HashMap<String, String>) -> Self {
177 self.is_template = true;
178 self.template_defaults = Some(defaults);
179 self
180 }
181
182 #[cfg(feature = "sdl-templates")]
184 pub fn with_variables(mut self, variables: std::collections::HashMap<String, String>) -> Self {
185 self.template_variables = Some(variables);
186 self
187 }
188
189 pub fn is_terminal(&self) -> bool {
191 matches!(self.step, Step::Complete | Step::Failed { .. })
192 }
193
194 pub fn is_failed(&self) -> bool {
196 matches!(self.step, Step::Failed { .. })
197 }
198
199 pub fn is_complete(&self) -> bool {
201 matches!(self.step, Step::Complete)
202 }
203
204 pub fn record_tx(&mut self, hash: impl Into<String>) {
206 self.tx_hashes.push(hash.into());
207 self.updated_at = current_unix_time();
208 }
209
210 pub fn transition(&mut self, step: Step) {
212 self.step = step;
213 self.updated_at = current_unix_time();
214 }
215
216 pub fn fail(&mut self, reason: impl Into<String>, recoverable: bool) {
218 self.step = Step::Failed {
219 reason: reason.into(),
220 recoverable,
221 };
222 self.updated_at = current_unix_time();
223 }
224}
225
226fn current_unix_time() -> u64 {
227 std::time::SystemTime::now()
228 .duration_since(std::time::UNIX_EPOCH)
229 .unwrap_or_default()
230 .as_secs()
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236
237 #[test]
238 fn test_new_state() {
239 let state = DeploymentState::new("session-1", "akash1abc...");
240 assert_eq!(state.session_id, "session-1");
241 assert_eq!(state.owner, "akash1abc...");
242 assert!(matches!(state.step, Step::Init));
243 assert!(!state.is_terminal());
244 }
245
246 #[test]
247 fn test_builder_pattern() {
248 let state = DeploymentState::new("s1", "owner")
249 .with_label("test-deploy")
250 .with_sdl("version: 2")
251 .with_deposit(10_000_000);
252
253 assert_eq!(state.label, "test-deploy");
254 assert_eq!(state.sdl_content, Some("version: 2".to_string()));
255 assert_eq!(state.deposit_uakt, 10_000_000);
256 }
257
258 #[test]
259 fn test_terminal_states() {
260 let mut state = DeploymentState::new("s1", "owner");
261 assert!(!state.is_terminal());
262
263 state.transition(Step::Complete);
264 assert!(state.is_terminal());
265 assert!(state.is_complete());
266
267 state.fail("something broke", true);
268 assert!(state.is_terminal());
269 assert!(state.is_failed());
270 }
271
272 #[test]
273 fn test_step_names() {
274 assert_eq!(Step::Init.name(), "init");
276 assert_eq!(Step::CheckBalance.name(), "check_balance");
277 assert_eq!(Step::EnsureCertificate.name(), "ensure_certificate");
278 assert_eq!(Step::CreateDeployment.name(), "create_deployment");
279 assert_eq!(
280 Step::WaitForBids { waited_blocks: 0 }.name(),
281 "wait_for_bids"
282 );
283 assert_eq!(Step::SelectProvider.name(), "select_provider");
284 assert_eq!(Step::CreateLease.name(), "create_lease");
285 assert_eq!(Step::SendManifest.name(), "send_manifest");
286 assert_eq!(
287 Step::WaitForEndpoints { attempts: 0 }.name(),
288 "wait_for_endpoints"
289 );
290 assert_eq!(Step::Complete.name(), "complete");
291 assert_eq!(
292 Step::Failed {
293 reason: "test".to_string(),
294 recoverable: false
295 }
296 .name(),
297 "failed"
298 );
299 }
300}