ic_backup/workflow/ic_snapshot_metadata/
mod.rs1use crate::{
4 model::{
5 artifacts::ArtifactChecksumRecord,
6 attempt_journal::MutationReceiptRequest,
7 ic_snapshot_metadata::IcSnapshotMetadataRequest,
8 ic_snapshot_transfer_read::{
9 IcSnapshotTransferReadPayload, IcSnapshotTransferReadRequest,
10 IcSnapshotTransferReadResponse,
11 },
12 },
13 ops::persistence::{
14 AttemptJournalError, AttemptJournalGuard, ExecutionStageGuard,
15 ExecutionWorkflowPersistenceError,
16 },
17 policy::ic_snapshot_transfer_read::{IcSnapshotTransferReadReply, IcSnapshotTransferReadView},
18 ports::ic_snapshot_transfer_read::IcSnapshotTransferReadProvider,
19 workflow::ic_snapshot_transfer_read::{
20 IcSnapshotTransferReadExecutionError, read_snapshot_with,
21 },
22};
23use thiserror::Error;
24
25pub async fn read_snapshot_metadata<E: std::error::Error + 'static>(
53 stage: &ExecutionStageGuard<'_>,
54 operation_sequence: u64,
55 payload: &IcSnapshotMetadataRequest,
56 provider: &mut impl IcSnapshotTransferReadProvider,
57 admit: impl AsyncFnOnce(&IcSnapshotTransferReadRequest<'_, '_>) -> Result<(), E>,
58 qualify: impl AsyncFnOnce(
59 &IcSnapshotTransferReadRequest<'_, '_>,
60 &IcSnapshotTransferReadResponse,
61 ) -> Result<MutationReceiptRequest, E>,
62) -> Result<
63 (IcSnapshotTransferReadResponse, ArtifactChecksumRecord),
64 IcSnapshotMetadataExecutionError<E>,
65> {
66 let operation = stage
67 .plan()
68 .singleton_operation(operation_sequence)
69 .ok_or(IcSnapshotMetadataExecutionError::OriginalMismatch)?;
70 if operation.request() != payload.digest().hash() {
71 return Err(IcSnapshotMetadataExecutionError::OriginalMismatch);
72 }
73 let (response, settlement) = read_snapshot_with(
74 stage,
75 operation_sequence,
76 IcSnapshotTransferReadPayload::Metadata(payload),
77 provider,
78 admit,
79 async |journal, request, admitted| {
80 settle_metadata(stage, journal, request, admitted, qualify).await
81 },
82 )
83 .await?;
84 super::qualified_receipt::retain_reply(response, settlement, |source, response| {
85 IcSnapshotMetadataExecutionError::AfterReply { source, response }
86 })
87}
88
89async fn settle_metadata<E: std::error::Error + 'static>(
90 stage: &ExecutionStageGuard<'_>,
91 journal: &mut AttemptJournalGuard<'_>,
92 request: &IcSnapshotTransferReadRequest<'_, '_>,
93 admitted: &IcSnapshotTransferReadView<'_, '_>,
94 qualify: impl AsyncFnOnce(
95 &IcSnapshotTransferReadRequest<'_, '_>,
96 &IcSnapshotTransferReadResponse,
97 ) -> Result<MutationReceiptRequest, E>,
98) -> Result<ArtifactChecksumRecord, IcSnapshotMetadataSettlementError<E>> {
99 let receipt = qualify(request, admitted.response())
100 .await
101 .map_err(IcSnapshotMetadataSettlementError::Qualification)?;
102 let evidence = super::qualified_receipt::record_applied(
103 journal,
104 receipt,
105 request.mutation_attempt(),
106 request.payload().digest().hash(),
107 IcSnapshotMetadataSettlementError::ReceiptRequired,
108 || {
109 let digest = match admitted.reply() {
110 IcSnapshotTransferReadReply::Metadata(metadata) => metadata.digest(),
111 IcSnapshotTransferReadReply::Data(_) => {
112 return Err(IcSnapshotMetadataSettlementError::ReceiptRequired);
113 }
114 };
115 stage.layout()?;
116 Ok(digest)
117 },
118 )?;
119 stage.layout()?;
120 Ok(evidence)
121}
122
123#[derive(Debug, Error)]
125pub enum IcSnapshotMetadataExecutionError<E: std::error::Error + 'static> {
126 #[error("snapshot metadata requires an exact singleton original stage")]
128 OriginalMismatch,
129 #[error(transparent)]
131 Read(#[from] IcSnapshotTransferReadExecutionError<E>),
132 #[error("snapshot metadata reply settlement failed: {source}")]
134 AfterReply {
135 source: IcSnapshotMetadataSettlementError<E>,
137 response: Box<IcSnapshotTransferReadResponse>,
139 },
140}
141
142#[derive(Debug, Error)]
144pub enum IcSnapshotMetadataSettlementError<E: std::error::Error + 'static> {
145 #[error("snapshot metadata qualification failed: {0}")]
147 Qualification(#[source] E),
148 #[error("snapshot metadata requires an explicit original Applied receipt")]
150 ReceiptRequired,
151 #[error(transparent)]
153 Stage(#[from] ExecutionWorkflowPersistenceError),
154 #[error(transparent)]
156 Journal(#[from] AttemptJournalError),
157}
158
159impl<E: std::error::Error + 'static> IcSnapshotMetadataExecutionError<E> {
160 #[must_use]
163 pub fn response(&self) -> Option<&IcSnapshotTransferReadResponse> {
164 match self {
165 Self::AfterReply { response, .. } => Some(response),
166 Self::Read(source) => source.response(),
167 Self::OriginalMismatch => None,
168 }
169 }
170}
171
172#[cfg(all(test, unix))]
173mod tests;