ic_backup/workflow/ic_snapshot_upload/data/
mod.rs1use super::{IcSnapshotUploadExecutionError, upload_snapshot_with};
4use crate::{
5 model::{
6 attempt_journal::MutationReceiptRequest,
7 evidence_digest::EvidenceDigest,
8 execution_workflow::ExecutionStagePredecessorRecord,
9 ic_mutation::IcMutationAcknowledgement,
10 ic_snapshot_upload::{
11 IcSnapshotDataUploadPlan, IcSnapshotDataUploadPlanningError, IcSnapshotUploadAttempt,
12 },
13 },
14 ops::persistence::{
15 AttemptJournalError, DownloadJournalGuard, ExecutionProgressPersistenceError,
16 ExecutionStageCheckpointError, ExecutionStageGuard, ExecutionWorkflowPersistenceError,
17 IcSnapshotUploadArtifactError, read_execution_progress,
18 },
19 ports::ic_snapshot_upload::IcSnapshotUploadProvider,
20};
21use thiserror::Error;
22
23pub async fn upload_snapshot_data<E: std::error::Error + 'static>(
52 stage: &ExecutionStageGuard<'_>,
53 upload: &IcSnapshotDataUploadPlan<'_, '_, '_>,
54 source: &DownloadJournalGuard<'_>,
55 snapshot: &str,
56 provider: &mut impl IcSnapshotUploadProvider,
57 mut admit: impl AsyncFnMut(&IcSnapshotUploadAttempt<'_, '_>) -> Result<(), E>,
58 mut qualify: impl AsyncFnMut(
59 &IcSnapshotUploadAttempt<'_, '_>,
60 &IcMutationAcknowledgement,
61 ) -> Result<MutationReceiptRequest, E>,
62) -> Result<ExecutionStagePredecessorRecord, IcSnapshotDataUploadExecutionError<E>> {
63 upload.validate_binding(stage.binding())?;
64 let plan = upload
65 .plan()
66 .ok_or(IcSnapshotDataUploadPlanningError::NoDataWrites)?;
67 if stage.plan() != plan {
68 return Err(IcSnapshotDataUploadPlanningError::OriginalMismatch.into());
69 }
70 let progress = read_execution_progress(stage.layout()?, &plan.digest())?;
71 if progress.attempts.mutations_used != 0 || progress.attempts.observations_used != 0 {
72 return Err(IcSnapshotDataUploadExecutionError::AlreadyAttempted);
73 }
74 let mut evidence = EvidenceDigest::new(b"ic-backup/ic-snapshot-data-upload/v1\0");
75 evidence.append(plan.digest().hash().as_bytes());
76 for (index, kind) in upload.kinds().iter().enumerate() {
77 let sequence =
78 u64::try_from(index).map_err(|_| IcSnapshotDataUploadPlanningError::CountOverflow)?;
79 let metadata = upload.metadata();
80 let payload = source.prepare_ic_snapshot_upload_data(
81 metadata.source_plan(),
82 snapshot,
83 metadata,
84 upload.destination(),
85 kind.clone(),
86 )?;
87 let (acknowledgement, settlement) = upload_snapshot_with(
89 stage,
90 sequence,
91 &payload,
92 provider,
93 &mut admit,
94 async |journal, request, admitted| {
95 super::super::qualified_receipt::AppliedReceiptRecorder {
96 journal,
97 expected_attempt: request.mutation_attempt(),
98 expected_request: payload.binding_digest().hash(),
99 }
100 .record_applied(
101 request,
102 admitted.acknowledgement(),
103 async |request, reply| qualify(request, reply).await,
104 IcSnapshotDataUploadReplyError::Qualification,
105 || IcSnapshotDataUploadReplyError::ReceiptRequired,
106 || {
107 stage
108 .layout()
109 .map(|_| ())
110 .map_err(IcSnapshotDataUploadReplyError::Stage)
111 },
112 )
113 .await?;
114 stage.layout()?;
115 Ok::<_, IcSnapshotDataUploadReplyError<E>>(admitted.reply().digest())
116 },
117 )
118 .await?;
119 let digest =
120 settlement.map_err(|source| IcSnapshotDataUploadExecutionError::AfterReply {
121 operation_sequence: sequence,
122 source,
123 acknowledgement: Box::new(acknowledgement),
124 })?;
125 evidence.append(digest.hash().as_bytes());
126 }
127 let progress = read_execution_progress(stage.layout()?, &plan.digest())?;
128 if progress.applied_operations != upload.kinds().len() {
129 return Err(IcSnapshotDataUploadExecutionError::AlreadyAttempted);
130 }
131 Ok(stage.reconcile_checkpoint(evidence.finish())?)
132}
133
134#[derive(Debug, Error)]
136pub enum IcSnapshotDataUploadExecutionError<E: std::error::Error + 'static> {
137 #[error("snapshot data upload original stage was already attempted")]
139 AlreadyAttempted,
140 #[error(transparent)]
142 Planning(#[from] IcSnapshotDataUploadPlanningError),
143 #[error(transparent)]
145 Stage(#[from] ExecutionWorkflowPersistenceError),
146 #[error(transparent)]
148 Progress(#[from] ExecutionProgressPersistenceError),
149 #[error(transparent)]
151 Source(#[from] IcSnapshotUploadArtifactError),
152 #[error(transparent)]
154 Upload(#[from] IcSnapshotUploadExecutionError<E>),
155 #[error("snapshot data upload reply rejected for operation {operation_sequence}: {source}")]
157 AfterReply {
158 operation_sequence: u64,
160 source: IcSnapshotDataUploadReplyError<E>,
162 acknowledgement: Box<IcMutationAcknowledgement>,
164 },
165 #[error(transparent)]
167 Checkpoint(#[from] ExecutionStageCheckpointError),
168}
169
170#[derive(Debug, Error)]
172pub enum IcSnapshotDataUploadReplyError<E: std::error::Error + 'static> {
173 #[error("snapshot data upload qualification failed: {0}")]
175 Qualification(#[source] E),
176 #[error("snapshot data upload requires an explicit original Applied receipt")]
178 ReceiptRequired,
179 #[error(transparent)]
181 Stage(#[from] ExecutionWorkflowPersistenceError),
182 #[error(transparent)]
184 Journal(#[from] AttemptJournalError),
185}