alien_core/deployment/
state.rs1use crate::{ObservedInventoryBatch, Platform, ResourceHeartbeat, StackState};
4use alien_error::AlienError;
5use bon::Builder;
6use serde::{Deserialize, Serialize};
7
8use super::{DeploymentStatus, EnvironmentInfo, ReleaseInfo};
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
14#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
15#[serde(rename_all = "camelCase")]
16pub struct RuntimeMetadata {
17 #[serde(skip_serializing_if = "Option::is_none")]
20 pub last_synced_env_vars_hash: Option<String>,
21
22 #[serde(skip_serializing_if = "Option::is_none")]
26 pub prepared_stack: Option<crate::Stack>,
27
28 #[serde(default, skip_serializing_if = "is_false")]
33 pub registry_access_granted: bool,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
44#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
45#[serde(rename_all = "camelCase")]
46pub struct DeploymentState {
47 pub status: DeploymentStatus,
49 pub platform: Platform,
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub current_release: Option<ReleaseInfo>,
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub target_release: Option<ReleaseInfo>,
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub stack_state: Option<StackState>,
60 #[serde(skip_serializing_if = "Option::is_none")]
64 pub error: Option<AlienError>,
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub environment_info: Option<EnvironmentInfo>,
68 #[serde(skip_serializing_if = "Option::is_none")]
70 pub runtime_metadata: Option<RuntimeMetadata>,
71 #[serde(default, skip_serializing_if = "is_false")]
74 pub retry_requested: bool,
75 pub protocol_version: u32,
80}
81
82impl DeploymentState {
83 pub fn has_desired(&self) -> bool {
86 self.current_release.is_some()
87 || self.target_release.is_some()
88 || self.stack_state.is_some()
89 }
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
97#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
98#[serde(rename_all = "camelCase")]
99pub struct DeploymentStepResult {
100 pub state: DeploymentState,
102
103 #[serde(skip_serializing_if = "Option::is_none")]
107 pub suggested_delay_ms: Option<u64>,
108
109 #[serde(default, skip_serializing_if = "is_false")]
113 pub update_heartbeat: bool,
114
115 #[serde(
117 default,
118 rename = "resourceHeartbeats",
119 skip_serializing_if = "Vec::is_empty"
120 )]
121 pub heartbeats: Vec<ResourceHeartbeat>,
122
123 #[serde(
125 default,
126 rename = "observedInventoryBatches",
127 skip_serializing_if = "Vec::is_empty"
128 )]
129 pub observed_inventory_batches: Vec<ObservedInventoryBatch>,
130}
131
132pub(crate) fn is_false(b: &bool) -> bool {
133 !*b
134}
135
136pub const MIN_SUPPORTED_DEPLOYMENT_PROTOCOL_VERSION: u32 = 1;
138
139pub const CURRENT_DEPLOYMENT_PROTOCOL_VERSION: u32 = 1;
142
143pub const DEPLOYMENT_PROTOCOL_VERSION: u32 = CURRENT_DEPLOYMENT_PROTOCOL_VERSION;
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use crate::{Platform, ReleaseInfo, Stack, StackState};
150 use indexmap::IndexMap;
151
152 fn empty_stack() -> Stack {
153 Stack {
154 id: "stack_test".to_string(),
155 resources: IndexMap::new(),
156 inputs: vec![],
157 permissions: crate::PermissionsConfig::default(),
158 supported_platforms: None,
159 }
160 }
161
162 fn release_info(id: &str) -> ReleaseInfo {
163 ReleaseInfo {
164 release_id: Some(id.to_string()),
165 version: None,
166 description: None,
167 stack: empty_stack(),
168 }
169 }
170
171 fn state() -> DeploymentState {
172 DeploymentState {
173 status: DeploymentStatus::Pending,
174 platform: Platform::Kubernetes,
175 current_release: None,
176 target_release: None,
177 stack_state: None,
178 error: None,
179 environment_info: None,
180 runtime_metadata: None,
181 retry_requested: false,
182 protocol_version: DEPLOYMENT_PROTOCOL_VERSION,
183 }
184 }
185
186 #[test]
187 fn deployment_state_has_desired_when_release_or_stack_state_exists() {
188 let observe_only = state();
189 assert!(!observe_only.has_desired());
190
191 let mut current = state();
192 current.current_release = Some(release_info("rel_current"));
193 assert!(current.has_desired());
194
195 let mut target = state();
196 target.target_release = Some(release_info("rel_target"));
197 assert!(target.has_desired());
198
199 let mut imported = state();
200 imported.stack_state = Some(StackState::new(Platform::Kubernetes));
201 assert!(imported.has_desired());
202 }
203}