1use super::inventory::ObservationStatusV1;
2use super::safety::SafetyFindingV1;
3use serde::{Deserialize, Serialize};
4
5#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
9pub struct DeploymentReceiptV1 {
10 pub schema_version: u32,
11 pub operation_id: String,
12 pub plan_id: String,
13 pub execution_context: Option<DeploymentExecutionContextV1>,
14 pub operation_status: DeploymentExecutionStatusV1,
15 pub started_at: String,
16 pub finished_at: Option<String>,
17 pub operator_principal: Option<String>,
18 pub root_principal: Option<String>,
19 pub previous_observed_deployment_epoch: Option<u64>,
20 pub phase_receipts: Vec<PhaseReceiptV1>,
21 pub role_phase_receipts: Vec<RolePhaseReceiptV1>,
22 pub final_inventory_id: Option<String>,
23 pub command_result: DeploymentCommandResultV1,
24}
25
26#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
30pub struct DeploymentExecutionContextV1 {
31 pub workspace_root: Option<String>,
32 pub icp_root: Option<String>,
33 pub artifact_roots: Vec<String>,
34 pub backend: DeploymentExecutorBackendV1,
35 pub backend_capabilities: Vec<DeploymentExecutorCapabilityV1>,
36}
37
38#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
42pub struct DeploymentExecutionPreflightV1 {
43 pub schema_version: u32,
44 pub plan_id: String,
45 pub safety_report_id: String,
46 pub authority_plan_id: String,
47 pub backend: DeploymentExecutorBackendV1,
48 pub status: DeploymentExecutionPreflightStatusV1,
49 pub planned_phases: Vec<String>,
50 pub required_capabilities: Vec<DeploymentExecutorCapabilityV1>,
51 pub missing_capabilities: Vec<DeploymentExecutorCapabilityV1>,
52 pub blockers: Vec<SafetyFindingV1>,
53}
54
55#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
59pub enum DeploymentExecutionPreflightStatusV1 {
60 Ready,
61 Blocked,
62}
63
64impl DeploymentExecutionPreflightStatusV1 {
65 #[must_use]
66 pub const fn label(self) -> &'static str {
67 match self {
68 Self::Ready => "ready",
69 Self::Blocked => "blocked",
70 }
71 }
72
73 #[must_use]
74 pub const fn variant_label(self) -> &'static str {
75 match self {
76 Self::Ready => "Ready",
77 Self::Blocked => "Blocked",
78 }
79 }
80}
81
82#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
86pub enum DeploymentExecutorBackendV1 {
87 CurrentCli,
88 PocketIc,
89 DirectAgent,
90 Other { name: String },
91}
92
93impl DeploymentExecutorBackendV1 {
94 #[must_use]
95 pub fn variant_label(&self) -> String {
96 match self {
97 Self::CurrentCli => "CurrentCli".to_string(),
98 Self::PocketIc => "PocketIc".to_string(),
99 Self::DirectAgent => "DirectAgent".to_string(),
100 Self::Other { name } => format!("Other {{ name: {name:?} }}"),
101 }
102 }
103}
104
105#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
109pub enum DeploymentExecutorCapabilityV1 {
110 CreateCanister,
111 CanisterStatus,
112 UpdateSettings,
113 InstallCode,
114 Call,
115 Query,
116 StageArtifact,
117}
118
119#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
123pub struct PhaseReceiptV1 {
124 pub phase: String,
125 pub started_at: String,
126 pub finished_at: Option<String>,
127 pub attempted_action: String,
128 pub verified_postcondition: VerifiedPostconditionV1,
129}
130
131#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
135pub struct VerifiedPostconditionV1 {
136 pub status: ObservationStatusV1,
137 pub evidence: Vec<String>,
138}
139
140#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
144pub enum DeploymentExecutionStatusV1 {
145 NotStarted,
146 InProgress,
147 FailedBeforeMutation,
148 PartiallyApplied,
149 FailedAfterMutation,
150 Complete,
151}
152
153impl DeploymentExecutionStatusV1 {
154 #[must_use]
155 pub const fn label(self) -> &'static str {
156 match self {
157 Self::NotStarted => "not_started",
158 Self::InProgress => "in_progress",
159 Self::FailedBeforeMutation => "failed_before_mutation",
160 Self::PartiallyApplied => "partially_applied",
161 Self::FailedAfterMutation => "failed_after_mutation",
162 Self::Complete => "complete",
163 }
164 }
165
166 #[must_use]
167 pub const fn variant_label(self) -> &'static str {
168 match self {
169 Self::NotStarted => "NotStarted",
170 Self::InProgress => "InProgress",
171 Self::FailedBeforeMutation => "FailedBeforeMutation",
172 Self::PartiallyApplied => "PartiallyApplied",
173 Self::FailedAfterMutation => "FailedAfterMutation",
174 Self::Complete => "Complete",
175 }
176 }
177}
178
179#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
183pub enum DeploymentCommandResultV1 {
184 NotFinished,
185 Succeeded,
186 Failed { code: String, message: String },
187}
188
189impl DeploymentCommandResultV1 {
190 #[must_use]
191 pub fn variant_label(&self) -> String {
192 match self {
193 Self::NotFinished => "NotFinished".to_string(),
194 Self::Succeeded => "Succeeded".to_string(),
195 Self::Failed { code, message } => {
196 format!("Failed {{ code: {code:?}, message: {message:?} }}")
197 }
198 }
199 }
200}
201
202#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
206pub struct RolePhaseReceiptV1 {
207 pub role: String,
208 pub phase: String,
209 pub result: RolePhaseResultV1,
210 pub previous_module_hash: Option<String>,
211 pub target_module_hash: Option<String>,
212 pub observed_module_hash_after: Option<String>,
213 pub artifact_digest: Option<String>,
214 pub canonical_embedded_config_sha256: Option<String>,
215 pub error: Option<String>,
216}
217
218#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
222pub enum RolePhaseResultV1 {
223 Applied,
224 Failed,
225 Skipped,
226 NotAttempted,
227 VerifiedAlreadyApplied,
228}
229
230impl RolePhaseResultV1 {
231 #[must_use]
232 pub const fn label(self) -> &'static str {
233 match self {
234 Self::Applied => "Applied",
235 Self::Failed => "Failed",
236 Self::Skipped => "Skipped",
237 Self::NotAttempted => "NotAttempted",
238 Self::VerifiedAlreadyApplied => "VerifiedAlreadyApplied",
239 }
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn deployment_execution_preflight_status_owns_text_labels() {
249 assert_eq!(DeploymentExecutionPreflightStatusV1::Ready.label(), "ready");
250 assert_eq!(
251 DeploymentExecutionPreflightStatusV1::Blocked.label(),
252 "blocked"
253 );
254 assert_eq!(
255 DeploymentExecutionPreflightStatusV1::Ready.variant_label(),
256 "Ready"
257 );
258 assert_eq!(
259 DeploymentExecutionPreflightStatusV1::Blocked.variant_label(),
260 "Blocked"
261 );
262 }
263
264 #[test]
265 fn deployment_executor_backend_owns_variant_labels() {
266 assert_eq!(
267 DeploymentExecutorBackendV1::CurrentCli.variant_label(),
268 "CurrentCli"
269 );
270 assert_eq!(
271 DeploymentExecutorBackendV1::PocketIc.variant_label(),
272 "PocketIc"
273 );
274 assert_eq!(
275 DeploymentExecutorBackendV1::DirectAgent.variant_label(),
276 "DirectAgent"
277 );
278 assert_eq!(
279 DeploymentExecutorBackendV1::Other {
280 name: "fixture".to_string()
281 }
282 .variant_label(),
283 "Other { name: \"fixture\" }"
284 );
285 }
286
287 #[test]
288 fn deployment_execution_status_owns_text_labels() {
289 assert_eq!(
290 DeploymentExecutionStatusV1::NotStarted.label(),
291 "not_started"
292 );
293 assert_eq!(
294 DeploymentExecutionStatusV1::InProgress.label(),
295 "in_progress"
296 );
297 assert_eq!(
298 DeploymentExecutionStatusV1::FailedBeforeMutation.label(),
299 "failed_before_mutation"
300 );
301 assert_eq!(
302 DeploymentExecutionStatusV1::PartiallyApplied.label(),
303 "partially_applied"
304 );
305 assert_eq!(
306 DeploymentExecutionStatusV1::FailedAfterMutation.label(),
307 "failed_after_mutation"
308 );
309 assert_eq!(DeploymentExecutionStatusV1::Complete.label(), "complete");
310 assert_eq!(
311 DeploymentExecutionStatusV1::NotStarted.variant_label(),
312 "NotStarted"
313 );
314 assert_eq!(
315 DeploymentExecutionStatusV1::InProgress.variant_label(),
316 "InProgress"
317 );
318 assert_eq!(
319 DeploymentExecutionStatusV1::FailedBeforeMutation.variant_label(),
320 "FailedBeforeMutation"
321 );
322 assert_eq!(
323 DeploymentExecutionStatusV1::PartiallyApplied.variant_label(),
324 "PartiallyApplied"
325 );
326 assert_eq!(
327 DeploymentExecutionStatusV1::FailedAfterMutation.variant_label(),
328 "FailedAfterMutation"
329 );
330 assert_eq!(
331 DeploymentExecutionStatusV1::Complete.variant_label(),
332 "Complete"
333 );
334 }
335
336 #[test]
337 fn deployment_command_result_owns_variant_labels() {
338 assert_eq!(
339 DeploymentCommandResultV1::NotFinished.variant_label(),
340 "NotFinished"
341 );
342 assert_eq!(
343 DeploymentCommandResultV1::Succeeded.variant_label(),
344 "Succeeded"
345 );
346 assert_eq!(
347 DeploymentCommandResultV1::Failed {
348 code: "install_failed".to_string(),
349 message: "install \"root\" failed".to_string(),
350 }
351 .variant_label(),
352 "Failed { code: \"install_failed\", message: \"install \\\"root\\\" failed\" }"
353 );
354 }
355
356 #[test]
357 fn role_phase_result_owns_text_labels() {
358 assert_eq!(RolePhaseResultV1::Applied.label(), "Applied");
359 assert_eq!(RolePhaseResultV1::Failed.label(), "Failed");
360 assert_eq!(RolePhaseResultV1::Skipped.label(), "Skipped");
361 assert_eq!(RolePhaseResultV1::NotAttempted.label(), "NotAttempted");
362 assert_eq!(
363 RolePhaseResultV1::VerifiedAlreadyApplied.label(),
364 "VerifiedAlreadyApplied"
365 );
366 }
367}