ic_backup/workflow/ic_snapshot_upload/allocation/
mod.rs1use super::{IcSnapshotUploadExecutionError, upload_snapshot_with};
4use crate::{
5 model::{
6 artifacts::ArtifactChecksumRecord,
7 attempt_journal::MutationReceiptRequest,
8 ic_mutation::IcMutationAcknowledgement,
9 ic_snapshot_upload::{
10 IcSnapshotUploadAttempt, IcSnapshotUploadKind, IcSnapshotUploadRequest,
11 },
12 },
13 ops::persistence::{
14 AttemptJournalError, ExecutionStageGuard, ExecutionWorkflowPersistenceError,
15 },
16 ports::ic_snapshot_upload::IcSnapshotUploadProvider,
17};
18use thiserror::Error;
19
20pub async fn allocate_snapshot<E: std::error::Error + 'static>(
47 stage: &ExecutionStageGuard<'_>,
48 operation_sequence: u64,
49 payload: &IcSnapshotUploadRequest<'_>,
50 provider: &mut impl IcSnapshotUploadProvider,
51 admit: impl AsyncFnOnce(&IcSnapshotUploadAttempt<'_, '_>) -> Result<(), E>,
52 qualify: impl AsyncFnOnce(
53 &IcSnapshotUploadAttempt<'_, '_>,
54 &IcMutationAcknowledgement,
55 ) -> Result<MutationReceiptRequest, E>,
56) -> Result<
57 (IcMutationAcknowledgement, ArtifactChecksumRecord),
58 IcSnapshotAllocationExecutionError<E>,
59> {
60 let operation = stage
61 .plan()
62 .singleton_operation(operation_sequence)
63 .ok_or(IcSnapshotAllocationExecutionError::OriginalMismatch)?;
64 if !matches!(payload.kind(), IcSnapshotUploadKind::Metadata)
65 || operation.request() != payload.binding_digest().hash()
66 {
67 return Err(IcSnapshotAllocationExecutionError::OriginalMismatch);
68 }
69 let (acknowledgement, settlement) = upload_snapshot_with(
70 stage,
71 operation_sequence,
72 payload,
73 provider,
74 admit,
75 async |journal, request, admitted| {
76 let receipt = qualify(request, admitted.acknowledgement())
77 .await
78 .map_err(IcSnapshotAllocationSettlementError::Qualification)?;
79 let evidence = super::super::qualified_receipt::record_applied(
80 journal,
81 receipt,
82 request.mutation_attempt(),
83 payload.binding_digest().hash(),
84 IcSnapshotAllocationSettlementError::ReceiptRequired,
85 || {
86 stage.layout()?;
87 Ok(admitted.reply().digest())
88 },
89 )?;
90 stage.layout()?;
91 Ok(evidence)
92 },
93 )
94 .await?;
95 super::super::qualified_receipt::retain_reply(
96 acknowledgement,
97 settlement,
98 |source, acknowledgement| IcSnapshotAllocationExecutionError::AfterReply {
99 source,
100 acknowledgement,
101 },
102 )
103}
104
105#[derive(Debug, Error)]
107pub enum IcSnapshotAllocationExecutionError<E: std::error::Error + 'static> {
108 #[error("snapshot allocation requires an exact singleton original metadata stage")]
110 OriginalMismatch,
111 #[error(transparent)]
113 Upload(#[from] IcSnapshotUploadExecutionError<E>),
114 #[error("snapshot allocation reply settlement failed: {source}")]
116 AfterReply {
117 source: IcSnapshotAllocationSettlementError<E>,
119 acknowledgement: Box<IcMutationAcknowledgement>,
121 },
122}
123
124#[derive(Debug, Error)]
126pub enum IcSnapshotAllocationSettlementError<E: std::error::Error + 'static> {
127 #[error("snapshot allocation qualification failed: {0}")]
129 Qualification(#[source] E),
130 #[error("snapshot allocation requires an explicit original Applied receipt")]
132 ReceiptRequired,
133 #[error(transparent)]
135 Stage(#[from] ExecutionWorkflowPersistenceError),
136 #[error(transparent)]
138 Journal(#[from] AttemptJournalError),
139}
140
141impl<E: std::error::Error + 'static> IcSnapshotAllocationExecutionError<E> {
142 #[must_use]
145 pub fn acknowledgement(&self) -> Option<&IcMutationAcknowledgement> {
146 match self {
147 Self::AfterReply {
148 acknowledgement, ..
149 } => Some(acknowledgement),
150 Self::Upload(source) => source.acknowledgement(),
151 Self::OriginalMismatch => None,
152 }
153 }
154}
155
156#[cfg(all(test, unix))]
157mod tests;