Skip to main content

canic_backup/execution/
types.rs

1use crate::plan::BackupOperationKind;
2use serde::{Deserialize, Serialize};
3use thiserror::Error as ThisError;
4
5///
6/// BackupExecutionJournal
7///
8
9#[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///
22/// BackupExecutionJournalOperation
23///
24
25#[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///
37/// BackupExecutionOperationState
38///
39
40#[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///
52/// BackupExecutionOperationReceipt
53///
54
55#[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    #[serde(default)]
68    pub snapshot_taken_at_timestamp: Option<u64>,
69    #[serde(default)]
70    pub snapshot_total_size_bytes: Option<u64>,
71    pub artifact_path: Option<String>,
72    pub checksum: Option<String>,
73    pub failure_reason: Option<String>,
74}
75
76///
77/// BackupExecutionOperationReceiptOutcome
78///
79
80#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
81#[serde(rename_all = "kebab-case")]
82pub enum BackupExecutionOperationReceiptOutcome {
83    Completed,
84    Failed,
85    Skipped,
86}
87
88///
89/// BackupExecutionResumeSummary
90///
91
92#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
93pub struct BackupExecutionResumeSummary {
94    pub plan_id: String,
95    pub run_id: String,
96    pub preflight_id: Option<String>,
97    pub preflight_accepted: bool,
98    pub restart_required: bool,
99    pub total_operations: usize,
100    pub ready_operations: usize,
101    pub pending_operations: usize,
102    pub blocked_operations: usize,
103    pub completed_operations: usize,
104    pub failed_operations: usize,
105    pub skipped_operations: usize,
106    pub next_operation: Option<BackupExecutionJournalOperation>,
107}
108
109///
110/// BackupExecutionJournalError
111///
112
113#[derive(Debug, ThisError)]
114pub enum BackupExecutionJournalError {
115    #[error("invalid backup plan for execution journal: {0}")]
116    InvalidPlan(String),
117
118    #[error("unsupported backup execution journal version {0}")]
119    UnsupportedVersion(u16),
120
121    #[error("backup execution journal field {0} is required")]
122    MissingField(&'static str),
123
124    #[error("backup execution journal has duplicate operation sequence {0}")]
125    DuplicateSequence(usize),
126
127    #[error("backup execution journal is missing operation sequence {0}")]
128    MissingSequence(usize),
129
130    #[error("accepted preflight is missing preflight_id")]
131    AcceptedPreflightMissingId,
132
133    #[error("restart_required does not match execution operation state")]
134    RestartRequiredMismatch,
135
136    #[error("preflight already accepted as {existing}, cannot accept {attempted}")]
137    PreflightAlreadyAccepted { existing: String, attempted: String },
138
139    #[error("preflight receipt plan id {actual} does not match execution journal plan {expected}")]
140    PreflightPlanMismatch { expected: String, actual: String },
141
142    #[error("mutating operation {sequence} is ready before preflight acceptance")]
143    MutationReadyBeforePreflight { sequence: usize },
144
145    #[error("mutating operation {sequence} cannot run before preflight acceptance")]
146    MutationBeforePreflightAccepted { sequence: usize },
147
148    #[error("operation {0} is missing a blocking or failure reason")]
149    OperationMissingReason(usize),
150
151    #[error("operation {0} cannot have blocking reasons in its current state")]
152    UnblockedOperationHasReasons(usize),
153
154    #[error("operation {0} was not found")]
155    OperationNotFound(usize),
156
157    #[error("operation {sequence} cannot transition from {from:?} to {to:?}")]
158    InvalidOperationTransition {
159        sequence: usize,
160        from: BackupExecutionOperationState,
161        to: BackupExecutionOperationState,
162    },
163
164    #[error("operation {requested} cannot advance before operation {next}")]
165    OutOfOrderOperationTransition { requested: usize, next: usize },
166
167    #[error("no operation can be advanced")]
168    NoTransitionableOperation,
169
170    #[error("operation {0} is not failed")]
171    OperationNotFailed(usize),
172
173    #[error("operation receipt references missing operation {0}")]
174    ReceiptOperationNotFound(usize),
175
176    #[error("operation receipt does not match operation {sequence}")]
177    ReceiptOperationMismatch { sequence: usize },
178
179    #[error("operation receipt does not match journal {sequence}")]
180    ReceiptJournalMismatch { sequence: usize },
181
182    #[error("operation receipt does not match accepted preflight {sequence}")]
183    ReceiptPreflightMismatch { sequence: usize },
184
185    #[error("operation receipt {sequence} has no pending operation")]
186    ReceiptWithoutPendingOperation { sequence: usize },
187}