ic_backup/workflow/ic_snapshot_capture/
mod.rs1use crate::{
4 model::{
5 ic_mutation::{IcMutationAcknowledgement, IcMutationRequest, IcMutationRequestError},
6 ic_request::{IcManagementMethodRecord, IcManagementRequestRecord},
7 operation_plan::OperationPlanError,
8 },
9 ops::persistence::{
10 AttemptJournalError, AttemptJournalGuard, ExecutionProgressPersistenceError,
11 ExecutionStageGuard, ExecutionWorkflowPersistenceError,
12 },
13 policy::ic_mutation::{IcMutationAssociationError, validate_acknowledgement},
14 ports::ic_mutation::{IcMutationProvider, IcMutationProviderError},
15};
16use thiserror::Error;
17
18pub fn capture_snapshot<E: std::error::Error + 'static>(
44 stage: &ExecutionStageGuard<'_>,
45 operation_sequence: u64,
46 payload: &IcManagementRequestRecord,
47 provider: &mut impl IcMutationProvider,
48 admit: impl FnOnce(&IcMutationRequest<'_>) -> Result<(), E>,
49) -> Result<IcMutationAcknowledgement, IcSnapshotCaptureExecutionError<E>> {
50 if payload.method() != IcManagementMethodRecord::TakeCanisterSnapshot {
51 return Err(IcSnapshotCaptureExecutionError::CaptureRequired);
52 }
53 let plan = stage.plan();
54 let authority = plan.attempt_authority(operation_sequence)?;
55 payload
56 .validate_mutation_binding(authority.binding())
57 .map_err(IcMutationRequestError::from)?;
58 let mut journal = AttemptJournalGuard::open(stage.layout()?, &authority)?;
59 journal.reserve_planned_mutation(&plan.digest())?;
60 let request = IcMutationRequest::new(plan, operation_sequence, journal.record()?, payload)?;
61 admit(&request).map_err(IcSnapshotCaptureExecutionError::Admission)?;
62 stage.layout()?;
63 let acknowledgement = provider.submit_mutation(&request)?;
64 let association = match journal.record() {
65 Ok(record) => record,
66 Err(source) => {
67 return Err(IcSnapshotCaptureExecutionError::AfterReplyJournal {
68 source,
69 acknowledgement: Box::new(acknowledgement),
70 });
71 }
72 };
73 if let Err(source) = validate_acknowledgement(&request, association, &acknowledgement) {
74 return Err(IcSnapshotCaptureExecutionError::Association {
75 source,
76 acknowledgement: Box::new(acknowledgement),
77 });
78 }
79 if let Err(source) = stage.layout() {
80 return Err(IcSnapshotCaptureExecutionError::AfterReplyStage {
81 source,
82 acknowledgement: Box::new(acknowledgement),
83 });
84 }
85 Ok(acknowledgement)
86}
87
88#[derive(Debug, Error)]
90pub enum IcSnapshotCaptureExecutionError<E: std::error::Error + 'static> {
91 #[error("snapshot capture requires take_canister_snapshot")]
93 CaptureRequired,
94 #[error(transparent)]
96 Plan(#[from] OperationPlanError),
97 #[error(transparent)]
99 Stage(#[from] ExecutionWorkflowPersistenceError),
100 #[error(transparent)]
102 Journal(#[from] AttemptJournalError),
103 #[error(transparent)]
105 Progress(#[from] ExecutionProgressPersistenceError),
106 #[error(transparent)]
108 Request(#[from] IcMutationRequestError),
109 #[error("fresh snapshot capture admission failed: {0}")]
111 Admission(#[source] E),
112 #[error(transparent)]
114 Provider(#[from] IcMutationProviderError),
115 #[error("snapshot capture acknowledgement association failed: {source}")]
117 Association {
118 source: IcMutationAssociationError,
120 acknowledgement: Box<IcMutationAcknowledgement>,
122 },
123 #[error("snapshot capture journal changed after reply: {source}")]
125 AfterReplyJournal {
126 source: AttemptJournalError,
128 acknowledgement: Box<IcMutationAcknowledgement>,
130 },
131 #[error("snapshot capture stage changed after reply: {source}")]
133 AfterReplyStage {
134 source: ExecutionWorkflowPersistenceError,
136 acknowledgement: Box<IcMutationAcknowledgement>,
138 },
139}
140
141#[cfg(all(test, unix))]
142mod tests;