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