Skip to main content

canic_backup/runner/
types.rs

1use crate::{
2    execution::{BackupExecutionJournalOperation, BackupExecutionResumeSummary},
3    plan::{BackupExecutionPreflightReceipts, BackupPlan},
4};
5use serde::Serialize;
6use std::{error::Error as StdError, fmt, path::Path, path::PathBuf};
7use thiserror::Error as ThisError;
8
9///
10/// BackupRunnerConfig
11///
12
13#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct BackupRunnerConfig {
15    pub out: PathBuf,
16    pub max_steps: Option<usize>,
17    pub updated_at: Option<String>,
18    pub tool_name: String,
19    pub tool_version: String,
20}
21
22///
23/// BackupRunnerExecutor
24///
25
26pub trait BackupRunnerExecutor {
27    /// Prove execution preflights before any mutating operation runs.
28    fn preflight_receipts(
29        &mut self,
30        plan: &BackupPlan,
31        preflight_id: &str,
32        validated_at: &str,
33        expires_at: &str,
34    ) -> Result<BackupExecutionPreflightReceipts, BackupRunnerCommandError>;
35
36    /// Stop one selected canister.
37    fn stop_canister(&mut self, canister_id: &str) -> Result<(), BackupRunnerCommandError>;
38
39    /// Start one selected canister.
40    fn start_canister(&mut self, canister_id: &str) -> Result<(), BackupRunnerCommandError>;
41
42    /// Create one selected canister snapshot and return the typed snapshot receipt.
43    fn create_snapshot(
44        &mut self,
45        canister_id: &str,
46    ) -> Result<BackupRunnerSnapshotReceipt, BackupRunnerCommandError>;
47
48    /// Download one selected snapshot into a temporary artifact directory.
49    fn download_snapshot(
50        &mut self,
51        canister_id: &str,
52        snapshot_id: &str,
53        artifact_path: &Path,
54    ) -> Result<(), BackupRunnerCommandError>;
55}
56
57///
58/// BackupRunnerSnapshotReceipt
59///
60
61#[derive(Clone, Debug, Eq, PartialEq)]
62pub struct BackupRunnerSnapshotReceipt {
63    pub snapshot_id: String,
64    pub taken_at_timestamp: Option<u64>,
65    pub total_size_bytes: Option<u64>,
66}
67
68///
69/// BackupRunnerCommandError
70///
71
72#[derive(Clone, Debug, Eq, PartialEq)]
73pub struct BackupRunnerCommandError {
74    pub status: String,
75    pub message: String,
76}
77
78impl BackupRunnerCommandError {
79    #[must_use]
80    pub fn failed(status: impl Into<String>, message: impl Into<String>) -> Self {
81        Self {
82            status: status.into(),
83            message: message.into(),
84        }
85    }
86}
87
88impl fmt::Display for BackupRunnerCommandError {
89    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
90        write!(formatter, "{}: {}", self.status, self.message)
91    }
92}
93
94impl StdError for BackupRunnerCommandError {}
95
96///
97/// BackupRunnerError
98///
99
100#[derive(Debug, ThisError)]
101pub enum BackupRunnerError {
102    #[error("backup execution journal is locked: {lock_path}")]
103    JournalLocked { lock_path: String },
104
105    #[error("backup operation {sequence} has no target canister")]
106    MissingOperationTarget { sequence: usize },
107
108    #[error("backup operation {sequence} has no snapshot id for target {target_canister_id}")]
109    MissingSnapshotId {
110        sequence: usize,
111        target_canister_id: String,
112    },
113
114    #[error(
115        "backup operation {sequence} has no artifact journal entry for target {target_canister_id}"
116    )]
117    MissingArtifactEntry {
118        sequence: usize,
119        target_canister_id: String,
120    },
121
122    #[error("backup operation {sequence} failed: {status}: {message}")]
123    CommandFailed {
124        sequence: usize,
125        status: String,
126        message: String,
127    },
128
129    #[error("backup preflight failed: {status}: {message}")]
130    PreflightFailed { status: String, message: String },
131
132    #[error("backup execution has no operation ready to run")]
133    NoReadyOperation,
134
135    #[error("backup execution is blocked: {reasons:?}")]
136    Blocked { reasons: Vec<String> },
137
138    #[error(transparent)]
139    Io(#[from] std::io::Error),
140
141    #[error(transparent)]
142    Json(#[from] serde_json::Error),
143
144    #[error(transparent)]
145    Persistence(#[from] crate::persistence::PersistenceError),
146
147    #[error(transparent)]
148    BackupPlan(#[from] crate::plan::BackupPlanError),
149
150    #[error(transparent)]
151    ExecutionJournal(#[from] crate::execution::BackupExecutionJournalError),
152
153    #[error(transparent)]
154    Journal(#[from] crate::journal::JournalValidationError),
155
156    #[error(transparent)]
157    Checksum(#[from] crate::artifacts::ArtifactChecksumError),
158
159    #[error(transparent)]
160    Manifest(#[from] crate::manifest::ManifestValidationError),
161}
162
163///
164/// BackupRunResponse
165///
166
167#[derive(Clone, Debug, Serialize)]
168pub struct BackupRunResponse {
169    pub run_id: String,
170    pub plan_id: String,
171    pub backup_id: String,
172    pub complete: bool,
173    pub max_steps_reached: bool,
174    pub executed_operation_count: usize,
175    pub executed_operations: Vec<BackupRunExecutedOperation>,
176    pub execution: BackupExecutionResumeSummary,
177}
178
179///
180/// BackupRunExecutedOperation
181///
182
183#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
184pub struct BackupRunExecutedOperation {
185    pub sequence: usize,
186    pub operation_id: String,
187    pub kind: String,
188    pub target_canister_id: Option<String>,
189    pub outcome: String,
190}
191
192impl BackupRunExecutedOperation {
193    pub(super) fn completed(operation: &BackupExecutionJournalOperation) -> Self {
194        Self::from_operation(operation, "completed")
195    }
196
197    pub(super) fn failed(operation: &BackupExecutionJournalOperation) -> Self {
198        Self::from_operation(operation, "failed")
199    }
200
201    fn from_operation(operation: &BackupExecutionJournalOperation, outcome: &str) -> Self {
202        Self {
203            sequence: operation.sequence,
204            operation_id: operation.operation_id.clone(),
205            kind: format!("{:?}", operation.kind),
206            target_canister_id: operation.target_canister_id.clone(),
207            outcome: outcome.to_string(),
208        }
209    }
210}