use crate::{ObservedInventoryBatch, Platform, ResourceHeartbeat, StackState};
use alien_error::AlienError;
use bon::Builder;
use serde::{Deserialize, Serialize};
use super::{DeploymentStatus, EnvironmentInfo, ReleaseInfo};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct RuntimeMetadata {
#[serde(skip_serializing_if = "Option::is_none")]
pub last_synced_env_vars_hash: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prepared_stack: Option<crate::Stack>,
#[serde(default, skip_serializing_if = "is_false")]
pub registry_access_granted: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct DeploymentState {
pub status: DeploymentStatus,
pub platform: Platform,
#[serde(skip_serializing_if = "Option::is_none")]
pub current_release: Option<ReleaseInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_release: Option<ReleaseInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stack_state: Option<StackState>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<AlienError>,
#[serde(skip_serializing_if = "Option::is_none")]
pub environment_info: Option<EnvironmentInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub runtime_metadata: Option<RuntimeMetadata>,
#[serde(default, skip_serializing_if = "is_false")]
pub retry_requested: bool,
pub protocol_version: u32,
}
impl DeploymentState {
pub fn has_desired(&self) -> bool {
self.current_release.is_some()
|| self.target_release.is_some()
|| self.stack_state.is_some()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct DeploymentStepResult {
pub state: DeploymentState,
#[serde(skip_serializing_if = "Option::is_none")]
pub suggested_delay_ms: Option<u64>,
#[serde(default, skip_serializing_if = "is_false")]
pub update_heartbeat: bool,
#[serde(
default,
rename = "resourceHeartbeats",
skip_serializing_if = "Vec::is_empty"
)]
pub heartbeats: Vec<ResourceHeartbeat>,
#[serde(
default,
rename = "observedInventoryBatches",
skip_serializing_if = "Vec::is_empty"
)]
pub observed_inventory_batches: Vec<ObservedInventoryBatch>,
}
pub(crate) fn is_false(b: &bool) -> bool {
!*b
}
pub const MIN_SUPPORTED_DEPLOYMENT_PROTOCOL_VERSION: u32 = 1;
pub const CURRENT_DEPLOYMENT_PROTOCOL_VERSION: u32 = 1;
pub const DEPLOYMENT_PROTOCOL_VERSION: u32 = CURRENT_DEPLOYMENT_PROTOCOL_VERSION;
#[cfg(test)]
mod tests {
use super::*;
use crate::{Platform, ReleaseInfo, Stack, StackState};
use indexmap::IndexMap;
fn empty_stack() -> Stack {
Stack {
id: "stack_test".to_string(),
resources: IndexMap::new(),
inputs: vec![],
permissions: crate::PermissionsConfig::default(),
supported_platforms: None,
}
}
fn release_info(id: &str) -> ReleaseInfo {
ReleaseInfo {
release_id: Some(id.to_string()),
version: None,
description: None,
stack: empty_stack(),
}
}
fn state() -> DeploymentState {
DeploymentState {
status: DeploymentStatus::Pending,
platform: Platform::Kubernetes,
current_release: None,
target_release: None,
stack_state: None,
error: None,
environment_info: None,
runtime_metadata: None,
retry_requested: false,
protocol_version: DEPLOYMENT_PROTOCOL_VERSION,
}
}
#[test]
fn deployment_state_has_desired_when_release_or_stack_state_exists() {
let observe_only = state();
assert!(!observe_only.has_desired());
let mut current = state();
current.current_release = Some(release_info("rel_current"));
assert!(current.has_desired());
let mut target = state();
target.target_release = Some(release_info("rel_target"));
assert!(target.has_desired());
let mut imported = state();
imported.stack_state = Some(StackState::new(Platform::Kubernetes));
assert!(imported.has_desired());
}
}