1use crate::plan::BackupOperationKind;
8
9use serde::{Deserialize, Serialize};
10use thiserror::Error as ThisError;
11
12#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
20#[serde(deny_unknown_fields)]
21pub struct BackupExecutionJournal {
22 pub journal_version: u16,
23 pub plan_id: String,
24 pub run_id: String,
25 #[serde(deserialize_with = "crate::serialization::required_option")]
26 pub preflight_id: Option<String>,
27 pub preflight_accepted: bool,
28 pub restart_required: bool,
29 pub operations: Vec<BackupExecutionJournalOperation>,
30 pub operation_receipts: Vec<BackupExecutionOperationReceipt>,
31}
32
33#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
41#[serde(deny_unknown_fields)]
42pub struct BackupExecutionJournalOperation {
43 pub sequence: usize,
44 pub operation_id: String,
45 pub kind: BackupOperationKind,
46 #[serde(deserialize_with = "crate::serialization::required_option")]
47 pub target_canister_id: Option<String>,
48 pub state: BackupExecutionOperationState,
49 #[serde(deserialize_with = "crate::serialization::required_option")]
50 pub state_updated_at: Option<String>,
51 pub blocking_reasons: Vec<String>,
52}
53
54#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
62#[serde(rename_all = "kebab-case")]
63pub enum BackupExecutionOperationState {
64 Ready,
65 Pending,
66 Blocked,
67 Completed,
68 Failed,
69 Skipped,
70}
71
72#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
80#[serde(deny_unknown_fields)]
81pub struct BackupExecutionOperationReceipt {
82 pub plan_id: String,
83 pub run_id: String,
84 #[serde(deserialize_with = "crate::serialization::required_option")]
85 pub preflight_id: Option<String>,
86 pub sequence: usize,
87 pub operation_id: String,
88 pub kind: BackupOperationKind,
89 #[serde(deserialize_with = "crate::serialization::required_option")]
90 pub target_canister_id: Option<String>,
91 pub outcome: BackupExecutionOperationReceiptOutcome,
92 #[serde(deserialize_with = "crate::serialization::required_option")]
93 pub updated_at: Option<String>,
94 #[serde(deserialize_with = "crate::serialization::required_option")]
95 pub snapshot_id: Option<String>,
96 #[serde(deserialize_with = "crate::serialization::required_option")]
97 pub snapshot_taken_at_timestamp: Option<u64>,
98 #[serde(deserialize_with = "crate::serialization::required_option")]
99 pub snapshot_total_size_bytes: Option<u64>,
100 #[serde(deserialize_with = "crate::serialization::required_option")]
101 pub artifact_path: Option<String>,
102 #[serde(deserialize_with = "crate::serialization::required_option")]
103 pub checksum: Option<String>,
104 #[serde(deserialize_with = "crate::serialization::required_option")]
105 pub failure_reason: Option<String>,
106}
107
108#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
116#[serde(rename_all = "kebab-case")]
117pub enum BackupExecutionOperationReceiptOutcome {
118 Completed,
119 Failed,
120 Skipped,
121}
122
123#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
131#[serde(deny_unknown_fields)]
132pub struct BackupExecutionResumeSummary {
133 pub plan_id: String,
134 pub run_id: String,
135 pub preflight_id: Option<String>,
136 pub preflight_accepted: bool,
137 pub restart_required: bool,
138 pub total_operations: usize,
139 pub ready_operations: usize,
140 pub pending_operations: usize,
141 pub blocked_operations: usize,
142 pub completed_operations: usize,
143 pub failed_operations: usize,
144 pub skipped_operations: usize,
145 pub next_operation: Option<BackupExecutionJournalOperation>,
146}
147
148#[derive(Debug, ThisError)]
156pub enum BackupExecutionJournalError {
157 #[error("invalid backup plan for execution journal: {0}")]
158 InvalidPlan(String),
159
160 #[error("unsupported backup execution journal version {0}")]
161 UnsupportedVersion(u16),
162
163 #[error("backup execution journal field {0} is required")]
164 MissingField(&'static str),
165
166 #[error("backup execution journal has duplicate operation sequence {0}")]
167 DuplicateSequence(usize),
168
169 #[error("backup execution journal is missing operation sequence {0}")]
170 MissingSequence(usize),
171
172 #[error("accepted preflight is missing preflight_id")]
173 AcceptedPreflightMissingId,
174
175 #[error("restart_required does not match execution operation state")]
176 RestartRequiredMismatch,
177
178 #[error("preflight already accepted as {existing}, cannot accept {attempted}")]
179 PreflightAlreadyAccepted { existing: String, attempted: String },
180
181 #[error("preflight receipt plan id {actual} does not match execution journal plan {expected}")]
182 PreflightPlanMismatch { expected: String, actual: String },
183
184 #[error("mutating operation {sequence} is ready before preflight acceptance")]
185 MutationReadyBeforePreflight { sequence: usize },
186
187 #[error("mutating operation {sequence} cannot run before preflight acceptance")]
188 MutationBeforePreflightAccepted { sequence: usize },
189
190 #[error("operation {0} is missing a blocking or failure reason")]
191 OperationMissingReason(usize),
192
193 #[error("operation {0} cannot have blocking reasons in its current state")]
194 UnblockedOperationHasReasons(usize),
195
196 #[error("operation {0} was not found")]
197 OperationNotFound(usize),
198
199 #[error("operation {sequence} cannot transition from {from:?} to {to:?}")]
200 InvalidOperationTransition {
201 sequence: usize,
202 from: BackupExecutionOperationState,
203 to: BackupExecutionOperationState,
204 },
205
206 #[error("operation {requested} cannot advance before operation {next}")]
207 OutOfOrderOperationTransition { requested: usize, next: usize },
208
209 #[error("no operation can be advanced")]
210 NoTransitionableOperation,
211
212 #[error("operation {0} is not failed")]
213 OperationNotFailed(usize),
214
215 #[error("operation receipt references missing operation {0}")]
216 ReceiptOperationNotFound(usize),
217
218 #[error("operation receipt does not match operation {sequence}")]
219 ReceiptOperationMismatch { sequence: usize },
220
221 #[error("operation receipt does not match journal {sequence}")]
222 ReceiptJournalMismatch { sequence: usize },
223
224 #[error("operation receipt does not match accepted preflight {sequence}")]
225 ReceiptPreflightMismatch { sequence: usize },
226
227 #[error("operation receipt {sequence} has no pending operation")]
228 ReceiptWithoutPendingOperation { sequence: usize },
229}