//! Types representing CloudFormation stack events.
#![allow(clippy::module_name_repetitions)]
use aws_smithy_types_convert::date_time::DateTimeExt;
use chrono::{DateTime, Utc};
use crate::{status_reason::StatusReason, ResourceStatus, StackStatus, Status};
/// A stack event from the `DescribeStackEvents` API.
///
/// Stack events are represented as an enum because the API reports both events for the stack and
/// events for the resources in the stack, but these can have different sets of statuses.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum StackEvent {
/// An event for the stack itself.
Stack {
/// Current status of the stack.
resource_status: StackStatus,
/// The details of the event.
details: StackEventDetails,
},
Resource {
/// Current status of the resource.
resource_status: ResourceStatus,
/// The details of the event.
details: StackEventDetails,
},
}
impl StackEvent {
/// Get the resource status of the event.
///
/// This returns a trait object. You can match on `self` if you need the specific status type.
#[must_use]
pub fn resource_status(&self) -> &dyn Status {
match self {
Self::Stack {
resource_status, ..
} => resource_status,
Self::Resource {
resource_status, ..
} => resource_status,
}
}
/// Get the details of the event.
#[must_use]
pub fn details(&self) -> &StackEventDetails {
match self {
Self::Stack { details, .. } | Self::Resource { details, .. } => details,
}
}
/// Get the token passed to the operation that generated this event.
///
/// All events triggerd by a given stack operation are assigne dthe same client request token,
/// you can use to track operations. For example, if you execute a `CreateStack` operation with
/// the token `token1`, then all the `StackEvents` generated by that operation will have
/// `ClientRequestToken` set as `token1`.
///
/// In the console, stack operations display the client request token on the Events tab. Stack
/// operations that are initiated from the console use the token format
/// *Console-StackOperation-ID*, which helps you easily identify the stack operation. For
/// example, if you create a stack using the console, each stack event would be assigned the
/// same token in the following format:
/// `Console-CreateStack-7f59c3cf-00d2-40c7-b2ff-e75db0987002`.
#[must_use]
pub fn client_request_token(&self) -> Option<&str> {
self.details().client_request_token.as_deref()
}
/// Get the unique ID of this event.
#[must_use]
pub fn event_id(&self) -> &str {
self.details().event_id.as_str()
}
/// Get the logical name of the resource specified in the template.
#[must_use]
pub fn logical_resource_id(&self) -> &str {
self.details().logical_resource_id.as_str()
}
/// Get the name of unique identifier associated with the physical instance of the resource.
///
/// This is unset when a physical resource does not exist, e.g. when creation is still in
/// progress or has failed.
#[must_use]
pub fn physical_resource_id(&self) -> Option<&str> {
self.details().physical_resource_id.as_deref()
}
/// Get the success/failure message associated with the resource.
#[must_use]
pub fn resource_status_reason(&self) -> Option<&str> {
self.details().resource_status_reason.as_deref()
}
/// Get the type of resource.
#[must_use]
pub fn resource_type(&self) -> &str {
self.details().resource_type.as_str()
}
/// Get the unique ID of the instance of the stack.
#[must_use]
pub fn stack_id(&self) -> &str {
self.details().stack_id.as_str()
}
/// Get the name associated with the stack.
#[must_use]
pub fn stack_name(&self) -> &str {
self.details().stack_name.as_str()
}
/// Get the time the status was updated.
#[must_use]
pub fn timestamp(&self) -> &DateTime<Utc> {
&self.details().timestamp
}
/// Indicates whether or not an event is terminal.
///
/// A terminal event is the last one that will occur during the current stack operation. By
/// definition, the terminal event is an event for the stack itself with a terminal
/// [`StackStatus`].
#[must_use]
pub fn is_terminal(&self) -> bool {
if let Self::Stack {
resource_status, ..
} = self
{
resource_status.is_settled()
} else {
false
}
}
pub(crate) fn from_sdk(event: aws_sdk_cloudformation::model::StackEvent) -> Self {
let is_stack = event.physical_resource_id.as_deref() == event.stack_id.as_deref();
let resource_status = event
.resource_status
.expect("StackEvent without resource_status");
let details = StackEventDetails {
client_request_token: event.client_request_token,
event_id: event.event_id.expect("StackEvent without event_id"),
logical_resource_id: event
.logical_resource_id
.expect("StackEvent without logical_resource_id"),
physical_resource_id: event.physical_resource_id,
resource_status_reason: event.resource_status_reason,
resource_type: event
.resource_type
.expect("StackEvent without resource_type"),
stack_id: event.stack_id.expect("StackEvent without stack_id"),
stack_name: event.stack_name.expect("StackEvent without stack_name"),
timestamp: event
.timestamp
.expect("StackEvent without timestamp")
.to_chrono_utc(),
};
if is_stack {
Self::Stack {
resource_status: resource_status
.as_str()
.parse()
.expect("invalid stack status"),
details,
}
} else {
Self::Resource {
resource_status: resource_status
.as_str()
.parse()
.expect("invalid resource status"),
details,
}
}
}
}
/// Event details from the `DescribeStackEvents` API that are common for stack and resource events.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StackEventDetails {
/// The token passed to the operation that generated this event.
///
/// All events triggerd by a given stack operation are assigne dthe same client request token,
/// you can use to track operations. For example, if you execute a `CreateStack` operation with
/// the token `token1`, then all the `StackEvents` generated by that operation will have
/// `ClientRequestToken` set as `token1`.
///
/// In the console, stack operations display the client request token on the Events tab. Stack
/// operations that are initiated from the console use the token format
/// *Console-StackOperation-ID*, which helps you easily identify the stack operation. For
/// example, if you create a stack using the console, each stack event would be assigned the
/// same token in the following format:
/// `Console-CreateStack-7f59c3cf-00d2-40c7-b2ff-e75db0987002`.
pub client_request_token: Option<String>,
/// The unique ID of this event.
pub event_id: String,
/// The logical name of the resource specified in the template.
pub logical_resource_id: String,
/// The name or unique identifier associated with the physical instance of the resource.
///
/// This is unset when a physical resource does not exist, e.g. when creation is still in
/// progress or has failed.
pub physical_resource_id: Option<String>,
/// Success/failure message associated with the resource.
pub resource_status_reason: Option<String>,
/// Type of resource.
pub resource_type: String,
/// The unique ID of the instance of the stack.
pub stack_id: String,
/// The name associated with the stack.
pub stack_name: String,
/// Time the status was updated.
pub timestamp: DateTime<Utc>,
}
impl StackEventDetails {
/// Get the token passed to the operation that generated this event.
///
/// All events triggerd by a given stack operation are assigne dthe same client request token,
/// you can use to track operations. For example, if you execute a `CreateStack` operation with
/// the token `token1`, then all the `StackEvents` generated by that operation will have
/// `ClientRequestToken` set as `token1`.
///
/// In the console, stack operations display the client request token on the Events tab. Stack
/// operations that are initiated from the console use the token format
/// *Console-StackOperation-ID*, which helps you easily identify the stack operation. For
/// example, if you create a stack using the console, each stack event would be assigned the
/// same token in the following format:
/// `Console-CreateStack-7f59c3cf-00d2-40c7-b2ff-e75db0987002`.
#[must_use]
pub fn client_request_token(&self) -> Option<&str> {
self.client_request_token.as_deref()
}
/// Get the unique ID of this event.
#[must_use]
pub fn event_id(&self) -> &str {
self.event_id.as_str()
}
/// Get the logical name of the resource specified in the template.
#[must_use]
pub fn logical_resource_id(&self) -> &str {
self.logical_resource_id.as_str()
}
/// Get the name of unique identifier associated with the physical instance of the resource.
///
/// This is unset when a physical resource does not exist, e.g. when creation is still in
/// progress or has failed.
#[must_use]
pub fn physical_resource_id(&self) -> Option<&str> {
self.physical_resource_id.as_deref()
}
/// Get the success/failure message associated with the resource.
#[must_use]
pub fn resource_status_reason(&self) -> StatusReason {
StatusReason::new(self.resource_status_reason.as_deref())
}
/// Get the type of resource.
#[must_use]
pub fn resource_type(&self) -> &str {
self.resource_type.as_str()
}
/// Get the unique ID of the instance of the stack.
#[must_use]
pub fn stack_id(&self) -> &str {
self.stack_id.as_str()
}
/// Get the name associated with the stack.
#[must_use]
pub fn stack_name(&self) -> &str {
self.stack_name.as_str()
}
/// Get the time the status was updated.
#[must_use]
pub fn timestamp(&self) -> &DateTime<Utc> {
&self.timestamp
}
}