1use crate::plan::BackupOperationKind;
2use serde::{Deserialize, Serialize};
3use thiserror::Error as ThisError;
4
5#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
10pub struct BackupExecutionJournal {
11 pub journal_version: u16,
12 pub plan_id: String,
13 pub run_id: String,
14 pub preflight_id: Option<String>,
15 pub preflight_accepted: bool,
16 pub restart_required: bool,
17 pub operations: Vec<BackupExecutionJournalOperation>,
18 pub operation_receipts: Vec<BackupExecutionOperationReceipt>,
19}
20
21#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
26pub struct BackupExecutionJournalOperation {
27 pub sequence: usize,
28 pub operation_id: String,
29 pub kind: BackupOperationKind,
30 pub target_canister_id: Option<String>,
31 pub state: BackupExecutionOperationState,
32 pub state_updated_at: Option<String>,
33 pub blocking_reasons: Vec<String>,
34}
35
36#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
41#[serde(rename_all = "kebab-case")]
42pub enum BackupExecutionOperationState {
43 Ready,
44 Pending,
45 Blocked,
46 Completed,
47 Failed,
48 Skipped,
49}
50
51#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
56pub struct BackupExecutionOperationReceipt {
57 pub plan_id: String,
58 pub run_id: String,
59 pub preflight_id: Option<String>,
60 pub sequence: usize,
61 pub operation_id: String,
62 pub kind: BackupOperationKind,
63 pub target_canister_id: Option<String>,
64 pub outcome: BackupExecutionOperationReceiptOutcome,
65 pub updated_at: Option<String>,
66 pub snapshot_id: Option<String>,
67 pub artifact_path: Option<String>,
68 pub checksum: Option<String>,
69 pub failure_reason: Option<String>,
70}
71
72#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
77#[serde(rename_all = "kebab-case")]
78pub enum BackupExecutionOperationReceiptOutcome {
79 Completed,
80 Failed,
81 Skipped,
82}
83
84#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
89pub struct BackupExecutionResumeSummary {
90 pub plan_id: String,
91 pub run_id: String,
92 pub preflight_id: Option<String>,
93 pub preflight_accepted: bool,
94 pub restart_required: bool,
95 pub total_operations: usize,
96 pub ready_operations: usize,
97 pub pending_operations: usize,
98 pub blocked_operations: usize,
99 pub completed_operations: usize,
100 pub failed_operations: usize,
101 pub skipped_operations: usize,
102 pub next_operation: Option<BackupExecutionJournalOperation>,
103}
104
105#[derive(Debug, ThisError)]
110pub enum BackupExecutionJournalError {
111 #[error("invalid backup plan for execution journal: {0}")]
112 InvalidPlan(String),
113
114 #[error("unsupported backup execution journal version {0}")]
115 UnsupportedVersion(u16),
116
117 #[error("backup execution journal field {0} is required")]
118 MissingField(&'static str),
119
120 #[error("backup execution journal has duplicate operation sequence {0}")]
121 DuplicateSequence(usize),
122
123 #[error("backup execution journal is missing operation sequence {0}")]
124 MissingSequence(usize),
125
126 #[error("accepted preflight is missing preflight_id")]
127 AcceptedPreflightMissingId,
128
129 #[error("preflight already accepted as {existing}, cannot accept {attempted}")]
130 PreflightAlreadyAccepted { existing: String, attempted: String },
131
132 #[error("preflight receipt plan id {actual} does not match execution journal plan {expected}")]
133 PreflightPlanMismatch { expected: String, actual: String },
134
135 #[error("mutating operation {sequence} is ready before preflight acceptance")]
136 MutationReadyBeforePreflight { sequence: usize },
137
138 #[error("mutating operation {sequence} cannot run before preflight acceptance")]
139 MutationBeforePreflightAccepted { sequence: usize },
140
141 #[error("operation {0} is missing a blocking or failure reason")]
142 OperationMissingReason(usize),
143
144 #[error("operation {0} cannot have blocking reasons in its current state")]
145 UnblockedOperationHasReasons(usize),
146
147 #[error("operation {0} was not found")]
148 OperationNotFound(usize),
149
150 #[error("operation {sequence} cannot transition from {from:?} to {to:?}")]
151 InvalidOperationTransition {
152 sequence: usize,
153 from: BackupExecutionOperationState,
154 to: BackupExecutionOperationState,
155 },
156
157 #[error("operation {requested} cannot advance before operation {next}")]
158 OutOfOrderOperationTransition { requested: usize, next: usize },
159
160 #[error("no operation can be advanced")]
161 NoTransitionableOperation,
162
163 #[error("operation {0} is not failed")]
164 OperationNotFailed(usize),
165
166 #[error("operation receipt references missing operation {0}")]
167 ReceiptOperationNotFound(usize),
168
169 #[error("operation receipt does not match operation {sequence}")]
170 ReceiptOperationMismatch { sequence: usize },
171
172 #[error("operation receipt does not match journal {sequence}")]
173 ReceiptJournalMismatch { sequence: usize },
174
175 #[error("operation receipt does not match accepted preflight {sequence}")]
176 ReceiptPreflightMismatch { sequence: usize },
177
178 #[error("operation receipt {sequence} has no pending operation")]
179 ReceiptWithoutPendingOperation { sequence: usize },
180}