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 async fn capture_snapshot<E: std::error::Error + 'static>(
47 stage: &ExecutionStageGuard<'_>,
48 operation_sequence: u64,
49 payload: &IcManagementRequestRecord,
50 provider: &mut impl IcMutationProvider,
51 admit: impl AsyncFnOnce(&IcMutationRequest<'_>) -> Result<(), E>,
52) -> Result<IcMutationAcknowledgement, IcSnapshotCaptureExecutionError<E>> {
53 if payload.method() != IcManagementMethodRecord::TakeCanisterSnapshot {
54 return Err(IcSnapshotCaptureExecutionError::CaptureRequired);
55 }
56 let plan = stage.plan();
57 let authority = plan.attempt_authority(operation_sequence)?;
58 payload
59 .validate_mutation_binding(authority.binding())
60 .map_err(IcMutationRequestError::from)?;
61 let mut journal = AttemptJournalGuard::open(stage.layout()?, &authority)?;
62 journal.reserve_planned_mutation(&plan.digest())?;
63 let request = IcMutationRequest::new(plan, operation_sequence, journal.record()?, payload)?;
64 let acknowledgement = super::reserved_effect::submit_once(
65 &journal,
66 &request,
67 async |request| {
68 admit(request)
69 .await
70 .map_err(IcSnapshotCaptureExecutionError::Admission)
71 },
72 || {
73 stage
74 .layout()
75 .map(|_| ())
76 .map_err(IcSnapshotCaptureExecutionError::Stage)
77 },
78 async |request, record| {
79 provider
80 .submit_mutation(request, record)
81 .await
82 .map_err(IcSnapshotCaptureExecutionError::Provider)
83 },
84 IcSnapshotCaptureExecutionError::Journal,
85 )
86 .await?;
87 if let Err(error) = super::reserved_effect::check_returned(
88 stage,
89 &journal,
90 &request,
91 &acknowledgement,
92 |request, record, reply| validate_acknowledgement(request, record, reply).map(|_| ()),
93 ) {
94 return Err(match error {
95 super::reserved_effect::ReturnedError::Journal(source) => {
96 IcSnapshotCaptureExecutionError::AfterReplyJournal {
97 source,
98 acknowledgement: Box::new(acknowledgement),
99 }
100 }
101 super::reserved_effect::ReturnedError::Association(source) => {
102 IcSnapshotCaptureExecutionError::Association {
103 source,
104 acknowledgement: Box::new(acknowledgement),
105 }
106 }
107 super::reserved_effect::ReturnedError::Stage(source) => {
108 IcSnapshotCaptureExecutionError::AfterReplyStage {
109 source,
110 acknowledgement: Box::new(acknowledgement),
111 }
112 }
113 });
114 }
115 Ok(acknowledgement)
116}
117
118#[derive(Debug, Error)]
120pub enum IcSnapshotCaptureExecutionError<E: std::error::Error + 'static> {
121 #[error("snapshot capture requires take_canister_snapshot")]
123 CaptureRequired,
124 #[error(transparent)]
126 Plan(#[from] OperationPlanError),
127 #[error(transparent)]
129 Stage(#[from] ExecutionWorkflowPersistenceError),
130 #[error(transparent)]
132 Journal(#[from] AttemptJournalError),
133 #[error(transparent)]
135 Progress(#[from] ExecutionProgressPersistenceError),
136 #[error(transparent)]
138 Request(#[from] IcMutationRequestError),
139 #[error("fresh snapshot capture admission failed: {0}")]
141 Admission(#[source] E),
142 #[error(transparent)]
144 Provider(#[from] IcMutationProviderError),
145 #[error("snapshot capture acknowledgement association failed: {source}")]
147 Association {
148 source: IcMutationAssociationError,
150 acknowledgement: Box<IcMutationAcknowledgement>,
152 },
153 #[error("snapshot capture journal changed after reply: {source}")]
155 AfterReplyJournal {
156 source: AttemptJournalError,
158 acknowledgement: Box<IcMutationAcknowledgement>,
160 },
161 #[error("snapshot capture stage changed after reply: {source}")]
163 AfterReplyStage {
164 source: ExecutionWorkflowPersistenceError,
166 acknowledgement: Box<IcMutationAcknowledgement>,
168 },
169}
170
171#[cfg(all(test, unix))]
172mod tests;