Skip to main content

tatara_process/
status.rs

1//! `ProcessStatus` sub-structures — conditions, checked boundaries, Flux refs.
2
3use chrono::{DateTime, Utc};
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7use crate::boundary::Condition;
8
9/// Standard K8s Condition (shape of `metav1.Condition`).
10#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
11#[serde(rename_all = "camelCase")]
12pub struct ProcessCondition {
13    #[serde(rename = "type")]
14    pub type_: String,
15    pub status: String,
16    pub last_transition_time: DateTime<Utc>,
17    #[serde(default, skip_serializing_if = "Option::is_none")]
18    pub reason: Option<String>,
19    #[serde(default, skip_serializing_if = "Option::is_none")]
20    pub message: Option<String>,
21}
22
23impl ProcessCondition {
24    pub fn ready(reason: impl Into<String>, message: Option<String>) -> Self {
25        Self {
26            type_: "Ready".into(),
27            status: "True".into(),
28            last_transition_time: Utc::now(),
29            reason: Some(reason.into()),
30            message,
31        }
32    }
33
34    pub fn not_ready(reason: impl Into<String>, message: impl Into<String>) -> Self {
35        Self {
36            type_: "Ready".into(),
37            status: "False".into(),
38            last_transition_time: Utc::now(),
39            reason: Some(reason.into()),
40            message: Some(message.into()),
41        }
42    }
43
44    pub fn attested(root: &str) -> Self {
45        Self {
46            type_: "Attested".into(),
47            status: "True".into(),
48            last_transition_time: Utc::now(),
49            reason: Some("AttestationWritten".into()),
50            message: Some(format!("composed_root={root}")),
51        }
52    }
53}
54
55/// Reference to a FluxCD resource emitted as part of this Process.
56#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
57#[serde(rename_all = "camelCase")]
58pub struct FluxResourceRef {
59    pub api_version: String,
60    pub kind: String,
61    pub name: String,
62    pub namespace: String,
63    #[serde(default)]
64    pub ready: bool,
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub message: Option<String>,
67    #[serde(default, skip_serializing_if = "Option::is_none")]
68    pub last_check: Option<DateTime<Utc>>,
69}
70
71/// A boundary condition paired with its current satisfaction state.
72#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
73#[serde(rename_all = "camelCase")]
74pub struct CheckedCondition {
75    #[serde(flatten)]
76    pub condition: Condition,
77    pub satisfied: bool,
78    #[serde(default, skip_serializing_if = "Option::is_none")]
79    pub last_check: Option<DateTime<Utc>>,
80    #[serde(default, skip_serializing_if = "Option::is_none")]
81    pub message: Option<String>,
82}
83
84/// Summary of boundary verification.
85#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
86#[serde(rename_all = "camelCase")]
87pub struct BoundaryStatus {
88    #[serde(default)]
89    pub preconditions: Vec<CheckedCondition>,
90    #[serde(default)]
91    pub postconditions: Vec<CheckedCondition>,
92    /// Absolute deadline for VERIFY (derived from `spec.boundary.timeout`).
93    #[serde(default, skip_serializing_if = "Option::is_none")]
94    pub deadline: Option<DateTime<Utc>>,
95}
96
97/// Summary of compliance checks at the latest attestation.
98#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
99#[serde(rename_all = "camelCase")]
100pub struct ComplianceStatus {
101    #[serde(default, skip_serializing_if = "Option::is_none")]
102    pub baseline: Option<String>,
103    pub satisfied: u32,
104    pub violated: u32,
105    pub total: u32,
106    #[serde(default)]
107    pub violations: Vec<String>,
108}