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 snapshot id.
43    fn create_snapshot(&mut self, canister_id: &str) -> Result<String, BackupRunnerCommandError>;
44
45    /// Download one selected snapshot into a temporary artifact directory.
46    fn download_snapshot(
47        &mut self,
48        canister_id: &str,
49        snapshot_id: &str,
50        artifact_path: &Path,
51    ) -> Result<(), BackupRunnerCommandError>;
52}
53
54///
55/// BackupRunnerCommandError
56///
57
58#[derive(Clone, Debug, Eq, PartialEq)]
59pub struct BackupRunnerCommandError {
60    pub status: String,
61    pub message: String,
62}
63
64impl BackupRunnerCommandError {
65    #[must_use]
66    pub fn failed(status: impl Into<String>, message: impl Into<String>) -> Self {
67        Self {
68            status: status.into(),
69            message: message.into(),
70        }
71    }
72}
73
74impl fmt::Display for BackupRunnerCommandError {
75    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
76        write!(formatter, "{}: {}", self.status, self.message)
77    }
78}
79
80impl StdError for BackupRunnerCommandError {}
81
82///
83/// BackupRunnerError
84///
85
86#[derive(Debug, ThisError)]
87pub enum BackupRunnerError {
88    #[error("backup execution journal is locked: {lock_path}")]
89    JournalLocked { lock_path: String },
90
91    #[error("backup operation {sequence} has no target canister")]
92    MissingOperationTarget { sequence: usize },
93
94    #[error("backup operation {sequence} has no snapshot id for target {target_canister_id}")]
95    MissingSnapshotId {
96        sequence: usize,
97        target_canister_id: String,
98    },
99
100    #[error(
101        "backup operation {sequence} has no artifact journal entry for target {target_canister_id}"
102    )]
103    MissingArtifactEntry {
104        sequence: usize,
105        target_canister_id: String,
106    },
107
108    #[error("backup operation {sequence} failed: {status}: {message}")]
109    CommandFailed {
110        sequence: usize,
111        status: String,
112        message: String,
113    },
114
115    #[error("backup preflight failed: {status}: {message}")]
116    PreflightFailed { status: String, message: String },
117
118    #[error("backup execution has no operation ready to run")]
119    NoReadyOperation,
120
121    #[error("backup execution is blocked: {reasons:?}")]
122    Blocked { reasons: Vec<String> },
123
124    #[error(transparent)]
125    Io(#[from] std::io::Error),
126
127    #[error(transparent)]
128    Json(#[from] serde_json::Error),
129
130    #[error(transparent)]
131    Persistence(#[from] crate::persistence::PersistenceError),
132
133    #[error(transparent)]
134    BackupPlan(#[from] crate::plan::BackupPlanError),
135
136    #[error(transparent)]
137    ExecutionJournal(#[from] crate::execution::BackupExecutionJournalError),
138
139    #[error(transparent)]
140    Journal(#[from] crate::journal::JournalValidationError),
141
142    #[error(transparent)]
143    Checksum(#[from] crate::artifacts::ArtifactChecksumError),
144
145    #[error(transparent)]
146    Manifest(#[from] crate::manifest::ManifestValidationError),
147}
148
149///
150/// BackupRunResponse
151///
152
153#[derive(Clone, Debug, Serialize)]
154pub struct BackupRunResponse {
155    pub run_id: String,
156    pub plan_id: String,
157    pub backup_id: String,
158    pub complete: bool,
159    pub max_steps_reached: bool,
160    pub executed_operation_count: usize,
161    pub executed_operations: Vec<BackupRunExecutedOperation>,
162    pub execution: BackupExecutionResumeSummary,
163}
164
165///
166/// BackupRunExecutedOperation
167///
168
169#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
170pub struct BackupRunExecutedOperation {
171    pub sequence: usize,
172    pub operation_id: String,
173    pub kind: String,
174    pub target_canister_id: Option<String>,
175    pub outcome: String,
176}
177
178impl BackupRunExecutedOperation {
179    pub(super) fn completed(operation: &BackupExecutionJournalOperation) -> Self {
180        Self::from_operation(operation, "completed")
181    }
182
183    pub(super) fn failed(operation: &BackupExecutionJournalOperation) -> Self {
184        Self::from_operation(operation, "failed")
185    }
186
187    fn from_operation(operation: &BackupExecutionJournalOperation, outcome: &str) -> Self {
188        Self {
189            sequence: operation.sequence,
190            operation_id: operation.operation_id.clone(),
191            kind: format!("{:?}", operation.kind),
192            target_canister_id: operation.target_canister_id.clone(),
193            outcome: outcome.to_string(),
194        }
195    }
196}